From 32940b7311d0459be70489bfe0b914f9af1fef36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Wed, 3 Apr 2024 11:41:33 +0300 Subject: [PATCH 01/26] merge with tiiuae, including refactoring and some file deletions (heavy resolve) --- build.sh | 39 + flake.lock | 103 +- hydrajobs/flake-module.nix | 1 + mk_patches.sh | 90 + .../agx-gpiovm-passthrough.nix | 62 + modules/jetpack-microvm/default.nix | 1 + .../jetpack/nvidia-jetson-orin/ssd-part.nix | 122 + .../common/gpio-virt-common/default.nix | 83 + ...2-vfio_platform-reset-required-false.patch | 24 + .../patches/0003-gpio-virt-kernel.patch | 4101 ++++++++ .../patches/0004-gpio-virt-drivers.patch | 1347 +++ .../patches/0005-gpio-overlay.patch | 18 + .../patches/0006-defconfig-kernel.patch | 8481 +++++++++++++++++ .../patches/0007-gpio-host-gpio-dts.patch | 62 + .../gpio-virt-common/patches/compile.log | 0 .../gpio-virt-common/patches/dummy.patch | 0 .../virtualization/default.nix | 2 + .../host/gpio-virt-host/default.nix | 75 + .../gpio-virt-host/gpio_pt_host_overlay.dtso | 17 + .../gpio-virt-host/overlays/qemu/default.nix | 11 + .../patches/0001-qemu-v8.1.3_gpio-virt.patch | 357 + .../patches/0002-gpio-host-uarta-dts.patch | 46 + modules/microvm/default.nix | 1 + .../microvm/virtualization/microvm/gpiovm.nix | 134 + targets/nvidia-jetson-orin/flake-module.nix | 1 + 25 files changed, 15135 insertions(+), 43 deletions(-) create mode 100755 build.sh create mode 100755 mk_patches.sh create mode 100644 modules/jetpack-microvm/agx-gpiovm-passthrough.nix create mode 100644 modules/jetpack/nvidia-jetson-orin/ssd-part.nix create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0002-vfio_platform-reset-required-false.patch create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0005-gpio-overlay.patch create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0006-defconfig-kernel.patch create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0007-gpio-host-gpio-dts.patch create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/compile.log create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/dummy.patch create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/gpio_pt_host_overlay.dtso create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/overlays/qemu/default.nix create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/overlays/qemu/patches/0001-qemu-v8.1.3_gpio-virt.patch create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/patches/0002-gpio-host-uarta-dts.patch create mode 100644 modules/microvm/virtualization/microvm/gpiovm.nix diff --git a/build.sh b/build.sh new file mode 100755 index 000000000..7bc01b127 --- /dev/null +++ b/build.sh @@ -0,0 +1,39 @@ +export MAKEOPTS="-j12" +flake='.#nvidia-jetson-orin-agx-debug' +echo "Do you want to build only or switch to a new nix flake? (b/t/s/x/e/n)" +if [ $# == '0' ] + then read ans + else ans=$1 +fi +export MAKEOPTS="-j12" +case $ans in + b|B) + echo "Building a nix derivation..." + nixos-rebuild --flake ${flake} build + ;; + t|T) + echo "Switchng a nix derivation..." + sudo nixos-rebuild --flake ${flake} switch + ;; + s|S) + echo "Switchng a nix derivation..." + sudo nixos-rebuild --flake ${flake} switch + ;; + x|X) + echo "Building a nix derivation..." + nixos-rebuild --flake ${flake} build --show-trace + ;; + e|E) + echo "Evaluating to eval_${flake}.tmp" + nix derivation show --recursive ${flake} > eval_${flake}.tmp + ;; + n|N) + echo "Exiting..." + exit 1 + ;; + *) + echo "Invalid input. Exiting..." + exit 1 + ;; +esac + diff --git a/flake.lock b/flake.lock index ccb9d84bd..8128a39aa 100644 --- a/flake.lock +++ b/flake.lock @@ -41,11 +41,11 @@ ] }, "locked": { - "lastModified": 1705332421, - "narHash": "sha256-USpGLPme1IuqG78JNqSaRabilwkCyHmVWY0M9vYyqEA=", + "lastModified": 1711099426, + "narHash": "sha256-HzpgM/wc3aqpnHJJ2oDqPBkNsqWbW0WfWUO8lKu8nGk=", "owner": "numtide", "repo": "devshell", - "rev": "83cb93d6d063ad290beee669f4badf9914cc16ec", + "rev": "2d45b54ca4a183f2fdcf4b19c895b64fbf620ee8", "type": "github" }, "original": { @@ -61,11 +61,11 @@ ] }, "locked": { - "lastModified": 1706491084, - "narHash": "sha256-eaEv+orTmr2arXpoE4aFZQMVPOYXCBEbLgK22kOtkhs=", + "lastModified": 1711934712, + "narHash": "sha256-sBDe+QmX/QohlnKeSEzrftcXyZL5FY09OMjZ59Rpyy4=", "owner": "nix-community", "repo": "disko", - "rev": "f67ba6552845ea5d7f596a24d57c33a8a9dc8de9", + "rev": "611c9ea53250f7bb22286b3d26872280a0e608f9", "type": "github" }, "original": { @@ -98,11 +98,11 @@ ] }, "locked": { - "lastModified": 1704982712, - "narHash": "sha256-2Ptt+9h8dczgle2Oo6z5ni5rt/uLMG47UFTR1ry/wgg=", + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "07f6395285469419cf9d078f59b5b49993198c00", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", "type": "github" }, "original": { @@ -151,11 +151,11 @@ ] }, "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -172,11 +172,11 @@ ] }, "locked": { - "lastModified": 1703887061, - "narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=", + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", "type": "github" }, "original": { @@ -192,11 +192,11 @@ ] }, "locked": { - "lastModified": 1707323143, - "narHash": "sha256-Mfj2l2aE+3Vu/u1M1PtQTvIoOZfCkINgtCQagSZFU6Q=", + "lastModified": 1711754718, + "narHash": "sha256-SSm/Hnwmrk1YNr1W8mWwYgD3cDcAolBL+9qSCoNtjdY=", "owner": "anduril", "repo": "jetpack-nixos", - "rev": "6ae4ce1d368fb56235a8b15ef926db28c4643eb8", + "rev": "5d99c74d2c4371a58e143aaa25d7ee3dbc832f79", "type": "github" }, "original": { @@ -247,14 +247,15 @@ ], "nixpkgs": [ "nixpkgs" - ] + ], + "spectrum": "spectrum" }, "locked": { - "lastModified": 1701202812, - "narHash": "sha256-ym/Rd4tR4i2d1WdPNKaeeIz/UoyfnCe5UBZbUl1M0PM=", + "lastModified": 1711753492, + "narHash": "sha256-lLlXQBebD6wd2m7vjXg2zQ8tfJF2a70RL+zXdbaQqn0=", "owner": "astro", "repo": "microvm.nix", - "rev": "89bb7a5230a4820736a43e058c8d2a2c560d672b", + "rev": "1b7c70b198554d0f0306ec153c94906623437aed", "type": "github" }, "original": { @@ -276,11 +277,11 @@ ] }, "locked": { - "lastModified": 1703607026, - "narHash": "sha256-Emh0BPoqlS4ntp2UJrwydXfIP4qIMF0VBB2FUE3/M/E=", + "lastModified": 1709911523, + "narHash": "sha256-XNutwbRI6h57ybeKy0yYupfngWYcfcIqE0b0LgXnyxs=", "owner": "Mic92", "repo": "nix-fast-build", - "rev": "4376b8a33b217ee2f78ba3dcff01a3e464d13a46", + "rev": "692fe3e98f36b60c678d637235271b57910a7f80", "type": "github" }, "original": { @@ -291,11 +292,11 @@ }, "nixlib": { "locked": { - "lastModified": 1693701915, - "narHash": "sha256-waHPLdDYUOHSEtMKKabcKIMhlUOHPOOPQ9UyFeEoovs=", + "lastModified": 1711846064, + "narHash": "sha256-cqfX0QJNEnge3a77VnytM0Q6QZZ0DziFXt6tSCV8ZSc=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "f5af57d3ef9947a70ac86e42695231ac1ad00c25", + "rev": "90b1a963ff84dc532db92f678296ff2499a60a87", "type": "github" }, "original": { @@ -312,11 +313,11 @@ ] }, "locked": { - "lastModified": 1705400161, - "narHash": "sha256-0MFaNIwwpVWB1N9m7cfHAM2pSVtYESQ7tlHxnDTOhM4=", + "lastModified": 1711932680, + "narHash": "sha256-CEpVtyB7uyRprTuiG+lpWWMvM/C0CbY/dbBuxT5BDwM=", "owner": "nix-community", "repo": "nixos-generators", - "rev": "521fb4cdd8a2e1a00d1adf0fea7135d1faf04234", + "rev": "1f4c32ca4295bb7cca1e48a2f39b65490b249b0b", "type": "github" }, "original": { @@ -327,11 +328,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1705312285, - "narHash": "sha256-rd+dY+v61Y8w3u9bukO/hB55Xl4wXv4/yC8rCGVnK5U=", + "lastModified": 1711352745, + "narHash": "sha256-luvqik+i3HTvCbXQZgB6uggvEcxI9uae0nmrgtXJ17U=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "bee2202bec57e521e3bd8acd526884b9767d7fa0", + "rev": "9a763a7acc4cfbb8603bb0231fec3eda864f81c0", "type": "github" }, "original": { @@ -342,11 +343,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1702914124, - "narHash": "sha256-EjmBJGB6DOdJnMoUKkNVMvJOYN11KU+U3IJcuGlhi38=", + "lastModified": 1711668574, + "narHash": "sha256-u1dfs0ASQIEr1icTVrsKwg2xToIpn7ZXxW3RHfHxshg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3a9928df838d1470a0e308cef74c251e90fc83a8", + "rev": "219951b495fc2eac67b1456824cc1ec1fd2ee659", "type": "github" }, "original": { @@ -373,11 +374,11 @@ ] }, "locked": { - "lastModified": 1705229514, - "narHash": "sha256-itILy0zimR/iyUGq5Dgg0fiW8plRDyxF153LWGsg3Cw=", + "lastModified": 1712055707, + "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "ffa9a5b90b0acfaa03b1533b83eaf5dead819a05", + "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a", "type": "github" }, "original": { @@ -431,6 +432,22 @@ "type": "github" } }, + "spectrum": { + "flake": false, + "locked": { + "lastModified": 1708358594, + "narHash": "sha256-e71YOotu2FYA67HoC/voJDTFsiPpZNRwmiQb4f94OxQ=", + "ref": "refs/heads/main", + "rev": "6d0e73864d28794cdbd26ab7b37259ab0e1e044c", + "revCount": 614, + "type": "git", + "url": "https://spectrum-os.org/git/spectrum" + }, + "original": { + "type": "git", + "url": "https://spectrum-os.org/git/spectrum" + } + }, "systems": { "locked": { "lastModified": 1681028828, @@ -468,11 +485,11 @@ ] }, "locked": { - "lastModified": 1705659004, - "narHash": "sha256-XQsZudrb9u5Pw631U0tFYZkjq49CcwF24XT01vz2jPk=", + "lastModified": 1711963903, + "narHash": "sha256-N3QDhoaX+paWXHbEXZapqd1r95mdshxToGowtjtYkGI=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "8cd95da6c30852adb2a06c4b6bdacfe8b64a0a35", + "rev": "49dc4a92b02b8e68798abd99184f228243b6e3ac", "type": "github" }, "original": { diff --git a/hydrajobs/flake-module.nix b/hydrajobs/flake-module.nix index 3d76a62ae..30d763e7c 100644 --- a/hydrajobs/flake-module.nix +++ b/hydrajobs/flake-module.nix @@ -5,6 +5,7 @@ bpmpEnableModule = {lib, ...}: { ghaf.hardware.nvidia = { virtualization.enable = lib.mkForce true; + virtualization.host.gpio.enable = lib.mkForce true; virtualization.host.bpmp.enable = lib.mkForce true; passthroughs.host.uarta.enable = lib.mkForce true; }; diff --git a/mk_patches.sh b/mk_patches.sh new file mode 100755 index 000000000..50398d6bc --- /dev/null +++ b/mk_patches.sh @@ -0,0 +1,90 @@ +home="/home/$(id -un)" +# sw=${PWD} +sw="${home}/software" +tegra="${home}/software/Jetson/Linux_for_Tegra" +kern="${tegra}/sources/kernel" +ghaf="${sw}/ghaf" +#patchdir="${ghaf}/modules/hardware/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches" +patchdir="${ghaf}/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/" + + +# create empty tree to git diff against +empty=$(git hash-object -t tree /dev/null) + +# make diff for dts files +# don't patch dts -- we are using overlays +#. dtsi_patch.sh + +# ------ + +pushd $kern + +# ------ + +# create patch for merged Kconfig and Makefile in gpio-virt +# merge gpio-virt/drivers and kernel-5.10/drivers Makefile and Kconfig file + +cp gpio-virt/drivers/Kconfig /tmp/original_Kconfig +cp gpio-virt/drivers/Makefile /tmp/original_Makefile + +# "cat" option leaves functions in gpio-virt #undeclared +#cat kernel-5.10/drivers/Kconfig gpio-virt/drivers/Kconfig >gpio-virt/drivers/tmp_Kconfig +grep -veendmenu kernel-5.10/drivers/Kconfig >gpio-virt/drivers/tmp_Kconfig +grep -veappend_menu gpio-virt/drivers/Kconfig >>gpio-virt/drivers/tmp_Kconfig +mv gpio-virt/drivers/tmp_Kconfig gpio-virt/drivers/Kconfig + +cat kernel-5.10/drivers/Makefile gpio-virt/drivers/Makefile >gpio-virt/drivers/tmp_Makefile +mv gpio-virt/drivers/tmp_Makefile gpio-virt/drivers/Makefile + +# Merged patch should be made against files in kernel-5-10 +# dont use ${empty} -- instead diff against merged files +git -C kernel-5.10/ diff -- drivers/Makefile ../gpio-virt/drivers/Makefile >${ghaf}/raw_MK_drivers.patch +git -C kernel-5.10/ diff -- drivers/Kconfig ../gpio-virt/drivers/Kconfig >>${ghaf}/raw_MK_drivers.patch +sed -i -e's/..\/gpio-virt\///' ${ghaf}/raw_MK_drivers.patch + +# restore Kconfig and Makefile used in merged patch +mv /tmp/original_Kconfig gpio-virt/drivers/Kconfig +mv /tmp/original_Makefile gpio-virt/drivers/Makefile + +# ------ + +# 0002-vfio_platform-reset-required-false.patch # not needed because of kernel boot parameters + +# ------ + +# 0003-gpio-virt-kernel.patch # exclude /drive/Kconfig and drive/Makefile +git -C kernel-5.10/ diff basepoint -- drivers/gpio/ \ + >${patchdir}/0003-gpio-virt-kernel.patch +git -C kernel-5.10/ diff basepoint -- drivers/pinctrl/ \ + >>${patchdir}/0003-gpio-virt-kernel.patch +git -C kernel-5.10/ diff basepoint -- include/ \ + >>${patchdir}/0003-gpio-virt-kernel.patch + +# ------ + +# 0004-gpio-virt-drivers.patch +# include merged Kconfig and Makefile by using raw_MK_drivers.patch (note '>>') +mv ${ghaf}/raw_MK_drivers.patch ${patchdir}/0004-gpio-virt-drivers.patch +git -C gpio-virt/ diff ${empty} -- "drivers/gpio*" \ + >>${patchdir}/0004-gpio-virt-drivers.patch + +# ------ + +# 0005-gpio-overlay.patch # included in raw-kernel.patch -- not needed because we do not use overlay +git -C kernel-5.10/ diff basepoint -- "kernel*overlays.txt" \ + >${patchdir}/0005-gpio-overlay.patch + +# ------ + +# 0006-defconfig-kernel.patch # included in raw-kernel.patch +git -C kernel-5.10/ diff basepoint -- "arch/arm64/configs/defconfig" \ + >${patchdir}/0006-defconfig-kernel.patch + +# ------ + +cd ${ghaf} + +# ------ + +# build ghaf +. ${ghaf}/build.sh diff --git a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix new file mode 100644 index 000000000..9d3994a85 --- /dev/null +++ b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix @@ -0,0 +1,62 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + config, + ... +}: let + cfg = config.ghaf.hardware.nvidia.orin.agx; +in { + options.ghaf.hardware.nvidia.orin.agx.enableGPIOPassthrough = + lib.mkEnableOption + "GPIO passthrough to VM"; + config = lib.mkIf cfg.enableGPIOPassthrough { + # Orin AGX GPIO Passthrough + # Debug statement to log a message + + ghaf.virtualization.microvm.gpiovm.extraModules = [ + { + microvm.devices = [ + { + # GPIO passthrough uses a character device (/dev/vda). No need to specify? + } + ]; + microvm.kernelParams = [ + "rootwait" + "root=/dev/vda" + "console=ttyAMA0" + ]; + } + ]; + # No need to set host kernel boot params here + boot.kernelParams = [ + "iommu=pt" + "vfio.enable_unsafe_noiommu_mode=0" + "vfio_iommu_type1.allow_unsafe_interrupts=1" + "vfio_platform.reset_required=0" + ]; + + # No need to set host device tree here ??? + /* + hardware.deviceTree = { + # Enable hardware.deviceTree for handle host dtb overlays + enable = true; + # name = "tegra234-p3701-0000-p3737-0000.dtb"; + # name = "tegra234-p3701-host-passthrough.dtb"; + + # using overlay file: + overlays = [ + { + name = "gpio_pt_host_overlay"; + dtsFile = ./gpio_pt_host_overlay.dtso; + + # Apply overlay only to host passthrough device tree + # filter = "tegra234-gpio-host-proxy.dtb"; + # filter = "tegra234-p3701-0000-p3737-0000.dtb"; + filter = "tegra234-p3701-host-passthrough.dtb"; + } + ]; + }; + */ + }; +} diff --git a/modules/jetpack-microvm/default.nix b/modules/jetpack-microvm/default.nix index 7f8146272..b9624c13f 100644 --- a/modules/jetpack-microvm/default.nix +++ b/modules/jetpack-microvm/default.nix @@ -4,5 +4,6 @@ imports = [ ./nx-netvm-ethernet-pci-passthrough.nix ./agx-netvm-wlan-pci-passthrough.nix + ./agx-gpiovm-passthrough.nix ]; } diff --git a/modules/jetpack/nvidia-jetson-orin/ssd-part.nix b/modules/jetpack/nvidia-jetson-orin/ssd-part.nix new file mode 100644 index 000000000..67e76fff8 --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/ssd-part.nix @@ -0,0 +1,122 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# Configuration for NVIDIA Jetson Orin AGX/NX reference boards +{ lib, config, pkgs, system, ... }: +let + cfg = config.ghaf.hardware.nvidia.orin.ssd; + + # WARNING -- DANGER - do not use this code before careful review + # disk data may be destroyed + + # check if at least one of HOME or STORAGE already exists + # (condition can be changed to [ != "2" ] ) + # failing mount of STORE or HOME is not critical (unless STORE data is needed) + ssdPartScriptOld = '' + echo "Executing SSD partitioning script" + + unset ssd + # grep for SSD in /dev/disk/by-id + # found SSD disk "should" be a link to /dev/nvme0n1 + ssd=$(ls -X /dev/disk/by-id/*SSD* | head -1) + # ssd='/dev/nvme0n1' + # check if HOME or STORAGE partitions already exist + if [ "$ssd" ] && [ $(lsblk -o LABEL "$ssd" | grep -e"^HOME$" -e"^STORAGE$" | wc -l) == "0" ] + then + # calculate partition sizes + sectors=$(blockdev --getsz "$ssd") + # assuming 2024 Master Boot Record sectors. + let sect_25=($sectors-2048)/4 # ~25% of disk + + echo "Debug Alert -- Partitioning of the SSD would have been done on disk $ssd" + exit 0 + + # Apply fdisk commands + fdisk_cmd="g\nn\n\n1\n$sect_25\nn\n\n2\nw\n" + fdisk "$ssd" <<< "$fdisk_cmd" + + # label new partitions + e2label "$ssd""p1" HOME + e2label "$ssd""p2" STORE + + fdisk -l $ssd + + # TODO insert copy of /nix/store to STORE partition + + # formatting not needed with Nix fileSystems.autoFormat + fi + ''; + ssdPartScriptSimple = '' + echo "Executing SSD partitioning script" + + unset ssd + # grep for SSD in /dev/disk/by-id + # found SSD disk "should" be a link to /dev/nvme0n1 + ssd=$(ls -X /dev/disk/by-id/*SSD* | head -1) + # ssd='/dev/nvme0n1' + # check if SSD device and NIXOS_SD partition already exist + if [ "$ssd" ] && [ $(lsblk -o LABEL "$ssd" | grep -e"^NIXOS_SD$" | wc -l) == "0" ] + then + # calculate partition size + sectors=$(blockdev --getsz "$ssd") + + echo "Debug Alert -- Partitioning of the SSD would have been done on disk $ssd" + exit 0 + + # Apply fdisk commands + fdisk_cmd="g\nn\n\n\n\ny\nw\nq\n"" + fdisk "$ssd" <<< "$fdisk_cmd" + + # TODO: move also FIRMWARE partition to SSD + + # relabel old partition + e2label /dev/disk/by-label/NIXOS_SD NIXOS_SD_MMC + # label new partition + e2label "$ssd""p1" NIXOS_SD + + fdisk -l $ssd + + mkdir /root/mmc + mkdir /root/ssd + mkfs.ext4 /dev/disk/by-label/NIXOS_SD + mount /dev/disk/by-label/NIXOS_SD_MMC /root/mmc + mount /dev/disk/by-label/NIXOS_SD /root/ssd + cp -a /root/mmc/* /root/ssd/ + umount /root/mmc + umount /root/ssd + rmdir /root/mmc + rmdir /root/ssd + fi + ''; +in +{ + options.ghaf.hardware.nvidia.orin.ssd.enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Enable partititioning and setup of SSD. + Warning: Disk data may be lost! + ''; + }; + + config = lib.mkIf cfg.enable { + # executes script at every boot. Make sure script is safe for that. + boot.postBootCommands = ssdPartScriptSimple; + /* + fileSystems = { + "/home" = { + autoFormat = true; + label = "HOME"; + # device = "/dev/disk/by-label/HOME"; + fsType = "ext4"; + }; + "/nix/store" = { + autoFormat = true; + label = "STORE"; + # device = "/dev/disk/by-label/STORE"; + fsType = "ext4"; + }; + }; + */ + }; +} diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix new file mode 100644 index 000000000..cb40f6e35 --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix @@ -0,0 +1,83 @@ +# Copyright 2022-2023 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + config, + ... +}: let + cfg = config.ghaf.hardware.nvidia.virtualization; +in { + options.ghaf.hardware.nvidia.virtualization.enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Enable virtualization support for NVIDIA Orin + + This option is an implementation level detail and is toggled automatically + by modules that need it. Manually enabling this option is not recommended in + release builds. + ''; + }; + + config = lib.mkIf cfg.enable { + boot.kernelPatches = [ + { + name = "Added Configurations to Support GPIO passthrough"; + patch = null; + extraStructuredConfig = with lib.kernel; { + PCI_STUB = lib.mkDefault yes; + VFIO = lib.mkDefault yes; + VIRTIO_PCI = lib.mkDefault yes; + VIRTIO_MMIO = lib.mkDefault yes; + HOTPLUG_PCI = lib.mkDefault yes; + PCI_DEBUG = lib.mkDefault yes; + PCI_HOST_GENERIC = lib.mkDefault yes; + VFIO_IOMMU_TYPE1 = lib.mkDefault yes; + HOTPLUG_PCI_ACPI = lib.mkDefault yes; + PCI_HOST_COMMON = lib.mkDefault yes; + VFIO_PLATFORM = lib.mkDefault yes; + VIRTIO_PCI = lib.mkDefault yes; + VIRTIO_MMIO = lib.mkDefault yes; + VFIO = lib.mkDefault yes; + CONFIG_GPIO_TEGRA = lib.mkDefault yes; + CONFIG_GPIO_TEGRA186 = lib.mkDefault yes; + TEGRA_GPIO_GUEST_PROXY = lib.mkDefault no; + TEGRA_GPIO_HOST_PROXY = lib.mkDefault no; + }; + } + + { + name = "Vfio_platform Reset Required False"; + patch = ./patches/0002-vfio_platform-reset-required-false.patch; + } + { + name = "GPIO Support Virtualization"; + patch = ./patches/0003-gpio-virt-kernel.patch; + } + { + name = "GPIO Virt Drivers"; + patch = ./patches/0004-gpio-virt-drivers.patch; + } + /* the device tree customisation is implemeted as an overlay -- not a patch + { + name = "GPIO Overlay"; + patch = ./patches/0005-gpio-overlay.patch; + } + */ + /* kerenel configuration is direct not with a defconfig patch + { + name = "GPIO defconfig"; + patch = ./patches/0006-defconfig-kernel.patch; + } + */ + /* removed, because we use overelay files + { + name = "GPIO dtsfiles"; + patch = ./patches/0007-gpio-host-gpio-dts.patch; + } + */ + ]; + + boot.kernelParams = [ "iommu=pt" "vfio.enable_unsafe_noiommu_mode=0" "vfio_iommu_type1.allow_unsafe_interrupts=1" "vfio_platform.reset_required=0" ]; + }; +} diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0002-vfio_platform-reset-required-false.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0002-vfio_platform-reset-required-false.patch new file mode 100644 index 000000000..df060edb7 --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0002-vfio_platform-reset-required-false.patch @@ -0,0 +1,24 @@ +From 03c7ecfd16e46838ab70c43a18990ef3dd35d08c Mon Sep 17 00:00:00 2001 +From: Juan Pablo Ruiz +Date: Thu, 4 May 2023 12:19:37 +0400 +Subject: [PATCH 2/3] vfio_platform: reset required false + +--- + drivers/vfio/platform/vfio_platform.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/vfio/platform/vfio_platform.c b/drivers/vfio/platform/vfio_platform.c +index 1e2769010089..3eabe37f400d 100644 +--- a/drivers/vfio/platform/vfio_platform.c ++++ b/drivers/vfio/platform/vfio_platform.c +@@ -15,7 +15,7 @@ + #define DRIVER_AUTHOR "Antonios Motakis " + #define DRIVER_DESC "VFIO for platform devices - User Level meta-driver" + +-static bool reset_required = true; ++static bool reset_required = false; + module_param(reset_required, bool, 0444); + MODULE_PARM_DESC(reset_required, "override reset requirement (default: 1)"); + +-- +2.25.1 diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch new file mode 100644 index 000000000..79062ec33 --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch @@ -0,0 +1,4101 @@ +diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c +index f66fc17faee4..629bc13aa4c3 100644 +--- a/drivers/gpio/gpio-tegra.c ++++ b/drivers/gpio/gpio-tegra.c +@@ -58,13 +58,30 @@ + #define GPIO_INT_LVL_LEVEL_HIGH 0x000001 + #define GPIO_INT_LVL_LEVEL_LOW 0x000000 + ++// #define GPIO_DEBUG ++// #define GPIO_DEBUG_VERBOSE ++ ++#ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++#else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ + struct tegra_gpio_info; + + struct tegra_gpio_bank { + unsigned int bank; + unsigned int irq; + spinlock_t gpio_lock[4]; +-#ifdef CONFIG_PM_SLEEP ++ #ifdef CONFIG_PM_SLEEP + u32 cnf[4]; + u32 out[4]; + u32 oe[4]; +@@ -72,7 +89,7 @@ struct tegra_gpio_bank { + u32 int_lvl[4]; + u32 wake_enb[4]; + u32 dbc_enb[4]; +-#endif ++ #endif + u32 dbc_cnt[4]; + u32 cnf_init[4]; + u32 out_init[4]; +@@ -101,17 +118,23 @@ static struct tegra_gpio_info *gpio_info; + static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi, + u32 val, u32 reg) + { ++ deb_debug("\n"); ++ + writel_relaxed(val, tgi->regs + reg); + } + + static inline u32 tegra_gpio_readl(struct tegra_gpio_info *tgi, u32 reg) + { ++ deb_debug("\n"); ++ + return readl_relaxed(tgi->regs + reg); + } + + static unsigned int tegra_gpio_compose(unsigned int bank, unsigned int port, + unsigned int bit) + { ++ deb_debug("\n"); ++ + return (bank << 5) | ((port & 0x3) << 3) | (bit & 0x7); + } + +@@ -120,6 +143,8 @@ static void tegra_gpio_mask_write(struct tegra_gpio_info *tgi, u32 reg, + { + u32 val; + ++ deb_debug("\n"); ++ + val = 0x100 << GPIO_BIT(gpio); + if (value) + val |= 1 << GPIO_BIT(gpio); +@@ -135,6 +160,8 @@ static void tegra_gpio_save_gpio_state(unsigned int gpio) + u32 mask = BIT(GPIO_BIT(gpio)); + unsigned long flags; + ++ deb_debug("\n"); ++ + spin_lock_irqsave(&bank->gpio_lock[p], flags); + + bank->cnf_init[p] &= ~mask; +@@ -206,17 +233,23 @@ static void tegra_gpio_restore_gpio_state(unsigned int gpio) + + static void tegra_gpio_enable(struct tegra_gpio_info *tgi, unsigned int gpio) + { ++ deb_debug("\n"); ++ + tegra_gpio_mask_write(tgi, GPIO_MSK_CNF(tgi, gpio), gpio, 1); + } + + static int tegra_gpio_request(struct gpio_chip *chip, unsigned int offset) + { ++ deb_debug("\n"); ++ + tegra_gpio_save_gpio_state(offset); + return pinctrl_gpio_request(chip->base + offset); + } + + static void tegra_gpio_free(struct gpio_chip *chip, unsigned int offset) + { ++ deb_debug("\n"); ++ + pinctrl_gpio_free(chip->base + offset); + tegra_gpio_restore_gpio_state(offset); + } +@@ -226,6 +259,8 @@ static void tegra_gpio_set(struct gpio_chip *chip, unsigned int offset, + { + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); + ++ deb_debug("\n"); ++ + tegra_gpio_mask_write(tgi, GPIO_MSK_OUT(tgi, offset), offset, value); + } + +@@ -247,6 +282,8 @@ static int tegra_gpio_direction_input(struct gpio_chip *chip, + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); + int ret; + ++ deb_debug("\n"); ++ + tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0); + tegra_gpio_enable(tgi, offset); + +@@ -266,6 +303,8 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); + int ret; + ++ deb_debug("\n"); ++ + tegra_gpio_set(chip, offset, value); + tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1); + tegra_gpio_enable(tgi, offset); +@@ -286,6 +325,8 @@ static int tegra_gpio_get_direction(struct gpio_chip *chip, + u32 pin_mask = BIT(GPIO_BIT(offset)); + u32 cnf, oe; + ++ deb_debug("\n"); ++ + cnf = tegra_gpio_readl(tgi, GPIO_CNF(tgi, offset)); + if (!(cnf & pin_mask)) + return -EINVAL; +@@ -337,6 +378,8 @@ static int tegra_gpio_set_config(struct gpio_chip *chip, unsigned int offset, + { + u32 debounce; + ++ deb_debug("\n"); ++ + if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) + return -ENOTSUPP; + +@@ -348,6 +391,8 @@ static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) + { + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); + ++ deb_debug("\n"); ++ + return irq_find_mapping(tgi->irq_domain, offset); + } + +@@ -495,7 +540,7 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc) + + } + +-#ifdef CONFIG_PM_SLEEP ++ #ifdef CONFIG_PM_SLEEP + static void tegra_gpio_resume(void) + { + struct tegra_gpio_info *tgi = gpio_info; +@@ -593,7 +638,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) + #else + #define tegra_gpio_suspend NULL + #define tegra_gpio_resume NULL +-#endif ++ #endif + + static struct syscore_ops tegra_gpio_syscore_ops = { + .suspend = tegra_gpio_suspend, +@@ -602,7 +647,7 @@ static struct syscore_ops tegra_gpio_syscore_ops = { + .restore = tegra_gpio_resume, + }; + +-#ifdef CONFIG_DEBUG_FS ++ #ifdef CONFIG_DEBUG_FS + + #include + #include +@@ -616,6 +661,8 @@ static int tegra_dbg_gpio_show(struct seq_file *s, void *unused) + x = ' '; + y = 'A'; + ++ deb_debug("\n"); ++ + seq_printf(s, "Name:Bank:Port CNF OE OUT IN INT_STA INT_ENB INT_LVL\n"); + for (i = 0; i < tgi->bank_count; i++) { + for (j = 0; j < 4; j++) { +@@ -649,6 +696,8 @@ DEFINE_SHOW_ATTRIBUTE(tegra_dbg_gpio); + + static void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) + { ++ deb_debug("\n"); ++ + debugfs_create_file("tegra_gpio", 0444, NULL, tgi, + &tegra_dbg_gpio_fops); + } +@@ -659,7 +708,7 @@ static inline void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) + { + } + +-#endif ++ #endif + + static int tegra_gpio_probe(struct platform_device *pdev) + { +@@ -668,6 +717,8 @@ static int tegra_gpio_probe(struct platform_device *pdev) + unsigned int gpio, i, j; + int ret; + ++ deb_debug("\n"); ++ + tgi = devm_kzalloc(&pdev->dev, sizeof(*tgi), GFP_KERNEL); + if (!tgi) + return -ENODEV; +@@ -707,9 +758,9 @@ static int tegra_gpio_probe(struct platform_device *pdev) + tgi->ic.irq_unmask = tegra_gpio_irq_unmask; + tgi->ic.irq_set_type = tegra_gpio_irq_set_type; + tgi->ic.irq_shutdown = tegra_gpio_irq_shutdown; +-#ifdef CONFIG_PM_SLEEP ++ #ifdef CONFIG_PM_SLEEP + tgi->ic.irq_set_wake = tegra_gpio_irq_set_wake; +-#endif ++ #endif + + platform_set_drvdata(pdev, tgi); + +@@ -816,6 +867,7 @@ static struct platform_driver tegra_gpio_driver = { + + static int __init tegra_gpio_init(void) + { ++ deb_debug("\n"); + return platform_driver_register(&tegra_gpio_driver); + } + subsys_initcall(tegra_gpio_init); +diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c +index 5e57824b283e..e56a6b06f91e 100644 +--- a/drivers/gpio/gpio-tegra186.c ++++ b/drivers/gpio/gpio-tegra186.c +@@ -44,7 +44,7 @@ + GPIO_SCR_SEC_REN) + + /* control registers */ +-#define TEGRA186_GPIO_ENABLE_CONFIG 0x00 ++#define TEGRA186_GPIO_ENABLE_CONFIG 0x00 + #define TEGRA186_GPIO_ENABLE_CONFIG_ENABLE BIT(0) + #define TEGRA186_GPIO_ENABLE_CONFIG_OUT BIT(1) + #define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_NONE (0x0 << 2) +@@ -179,6 +179,51 @@ + + /**************************************************************/ + ++// possibly/probably declare this in gpio-tegra.c instead ++// following pattern from bpmp virtualisation ++// ++// TODO separate guest and host proxy configuration defines ++#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++ ++ #include "gpiolib.h" ++ #include ++ ++ #define GPIO_DEBUG ++ #define GPIO_DEBUG_VERBOSE ++ ++ #ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++ #endif ++ ++ #ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++ #else ++ #define deb_verbose(fmt, ...) ++ #endif ++ ++ int gpio_outloud = 0; ++ EXPORT_SYMBOL_GPL(gpio_outloud); ++ ++ uint64_t gpio_vpa = 0; ++ ++ extern struct gpio_chip *find_chip_by_name(const char *name); ++ extern const char **tegra_chiplabel; ++ ++#endif ++ ++/* this portion of code comes from copydrivers branch ++// structures to synchronise get_tegra186_gpio_driver() ++// it allows proxy drivers to copy preset gpio and driver to themselves ++static DECLARE_COMPLETION(gpio_data_ready); ++static DEFINE_SPINLOCK(gpio_data_lock); ++ ++static struct tegra_gpio preset_gpio_local[2]; ++*/ ++ + struct tegra_gpio_port { + const char *name; + unsigned int bank; +@@ -192,25 +237,25 @@ struct tegra186_pin_range { + }; + + struct tegra_gpio_soc { +- const struct tegra_gpio_port *ports; +- unsigned int num_ports; +- const char *name; +- unsigned int instance; +- unsigned int num_irqs_per_bank; +- bool is_hw_ts_sup; +- bool do_vm_check; +- const struct tegra186_pin_range *pin_ranges; +- unsigned int num_pin_ranges; +- const char *pinmux; +- const struct tegra_gte_info *gte_info; +- int gte_npins; ++ const struct tegra_gpio_port *ports; ++ unsigned int num_ports; ++ const char *name; ++ unsigned int instance; ++ unsigned int num_irqs_per_bank; ++ bool is_hw_ts_sup; ++ bool do_vm_check; ++ const struct tegra186_pin_range *pin_ranges; ++ unsigned int num_pin_ranges; ++ const char *pinmux; ++ const struct tegra_gte_info *gte_info; ++ int gte_npins; + }; + + struct tegra_gpio_saved_register { +- bool restore_needed; +- u32 val; +- u32 conf; +- u32 out; ++ bool restore_needed; ++ u32 val; ++ u32 conf; ++ u32 out; + }; + + struct tegra_gpio { +@@ -278,12 +323,16 @@ static struct tegra_gte_info tegra194_gte_info[] = { + + static inline u32 tegra_gte_readl(struct tegra_gpio *tgi, u32 reg) + { ++ // deb_verbose("\n"); ++ + return __raw_readl(tgi->gte_regs + reg); + } + + static inline void tegra_gte_writel(struct tegra_gpio *tgi, u32 reg, + u32 val) + { ++ // deb_verbose("\n"); ++ + __raw_writel(val, tgi->gte_regs + reg); + } + +@@ -307,6 +356,8 @@ u64 tegra_gte_read_fifo(struct tegra_gpio *tgi, u32 offset) + u32 aon_bits; + u32 bit_index = 0; + ++ // deb_verbose("\n"); ++ + /* Check if FIFO is empty */ + while ((tegra_gte_readl(tgi, GTE_GPIO_TESTATUS) >> + GTE_GPIO_TESTATUS_OCCUPANCY_SHIFT) & +@@ -348,6 +399,8 @@ int tegra_gte_enable_ts(struct tegra_gpio *tgi, u32 offset) + u32 val, mask, reg; + int i = 0; + ++ // deb_verbose("\n"); ++ + if (tgi->gte_enable == 1) { + dev_err(tgi->gpio.parent, "timestamp is already enabled for gpio\n"); + return -EINVAL; +@@ -381,6 +434,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) + { + u32 val, mask; + ++ // deb_verbose("\n"); ++ + if (tgi->gte_enable == 0) { + dev_err(tgi->gpio.parent, "timestamp is already disabled\n"); + return 0; +@@ -405,6 +460,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) + + int tegra_gte_setup(struct tegra_gpio *tgi) + { ++ // deb_verbose("\n"); ++ + tegra_gte_writel(tgi, GTE_GPIO_TECTRL, 0); + tgi->gte_enable = 0; + +@@ -418,6 +475,8 @@ tegra186_gpio_get_port(struct tegra_gpio *gpio, unsigned int *pin) + { + unsigned int start = 0, i; + ++ // deb_verbose("GPIO, chip \n", gpio->gpio.label); ++ + for (i = 0; i < gpio->soc->num_ports; i++) { + const struct tegra_gpio_port *port = &gpio->soc->ports[i]; + +@@ -438,6 +497,8 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, + const struct tegra_gpio_port *port; + unsigned int offset; + ++ // deb_verbose("GPIO, chip \n", gpio->gpio.label); ++ + port = tegra186_gpio_get_port(gpio, &pin); + if (!port) + return NULL; +@@ -453,6 +514,8 @@ static void __iomem *tegra186_gpio_get_secure(struct tegra_gpio *gpio, + const struct tegra_gpio_port *port; + unsigned int offset; + ++ // deb_verbose("GPIO, chip \n", gpio->gpio.label); ++ + port = tegra186_gpio_get_port(gpio, &pin); + if (!port) + return NULL; +@@ -466,14 +529,17 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) + void __iomem *secure; + u32 val; + ++ // deb_verbose("\n"); ++ + secure = tegra186_gpio_get_secure(gpio, pin); + if (gpio->soc->do_vm_check) { +- val = __raw_readl(secure + GPIO_VM_REG); +- if ((val & GPIO_VM_RW) != GPIO_VM_RW) +- return false; ++ val = __raw_readl(secure + GPIO_VM_REG); ++ if ((val & GPIO_VM_RW) != GPIO_VM_RW) ++ return false; + } + +- val = __raw_readl(secure + GPIO_SCR_REG); ++ val = __raw_readl(secure + GPIO_SCR_REG); ++ // deb_verbose("val = 0x%X, val&mask = 0x%lX\n", val, (val & (GPIO_SCR_SEC_ENABLE))); + + if ((val & (GPIO_SCR_SEC_ENABLE)) == 0) + return true; +@@ -484,13 +550,15 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) + return false; + } + +-static int tegra186_gpio_get_direction(struct gpio_chip *chip, ++int tegra186_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) + { + struct tegra_gpio *gpio = gpiochip_get_data(chip); + void __iomem *base; + u32 value; + ++ deb_verbose("GPIO, chip %s, offset %u\n", chip->label, offset); ++ + if (!gpio_is_accessible(gpio, offset)) + return -EPERM; + +@@ -505,7 +573,7 @@ static int tegra186_gpio_get_direction(struct gpio_chip *chip, + return GPIO_LINE_DIRECTION_IN; + } + +-static int tegra186_gpio_direction_input(struct gpio_chip *chip, ++int tegra186_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) + { + struct tegra_gpio *gpio = gpiochip_get_data(chip); +@@ -513,6 +581,8 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, + u32 value; + int ret = 0; + ++ deb_verbose("GPIO, chip %s, offset %u\n", chip->label, offset); ++ + if (!gpio_is_accessible(gpio, offset)) + return -EPERM; + +@@ -536,7 +606,7 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, + return ret; + } + +-static int tegra186_gpio_direction_output(struct gpio_chip *chip, ++int tegra186_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int level) + { + struct tegra_gpio *gpio = gpiochip_get_data(chip); +@@ -544,6 +614,8 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, + u32 value; + int ret = 0; + ++ deb_verbose("GPIO, chip %s, offset %u, level %d\n", chip->label, offset, level); ++ + if (!gpio_is_accessible(gpio, offset)) + return -EPERM; + +@@ -578,6 +650,8 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, + struct tegra_gpio_saved_register *regs; + void __iomem *base; + ++ deb_verbose("GPIO chip %s, offset %u\n", chip->label, offset); ++ + if (!gpio_is_accessible(gpio, offset)) + return -EPERM; + +@@ -606,6 +680,8 @@ static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, + int value; + int ret; + ++ deb_verbose("GPIO, chip %s, offset %u\n", chip->label, offset); ++ + base = tegra186_gpio_get_base(gpio, offset); + if (WARN_ON(base == NULL)) + return -EINVAL; +@@ -630,6 +706,8 @@ static int tegra_gpio_timestamp_read(struct gpio_chip *chip, unsigned offset, + struct tegra_gpio *tgi = gpiochip_get_data(chip); + int ret; + ++ deb_verbose("GPIO chip %s, offset %u\n", chip->label, offset); ++ + if (tgi->use_timestamp) { + *ts = tegra_gte_read_fifo(tgi, offset); + ret = 0; +@@ -645,6 +723,8 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) + void __iomem *base; + u32 value; + ++ deb_verbose("GPIO chip %s, offset %u\n", chip->label, offset); ++ + base = tegra186_gpio_get_base(gpio, offset); + if (WARN_ON(base == NULL)) + return -ENODEV; +@@ -658,17 +738,21 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) + return value & BIT(0); + } + +-static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, ++void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, + int level) + { + struct tegra_gpio *gpio = gpiochip_get_data(chip); + void __iomem *base; + u32 value; + +- if (!gpio_is_accessible(gpio, offset)) +- return; ++ deb_verbose("(1) chip %s, Offset %d, Level %d\n", gpio->gpio.label, offset, level); + ++ if (!gpio_is_accessible(gpio, offset)) { ++ pr_err("GPIO error: gpio is not accessible, Chip %s, Offset %d", gpio->gpio.label, offset); ++ return; ++ } + base = tegra186_gpio_get_base(gpio, offset); ++ + if (WARN_ON(base == NULL)) + return; + +@@ -679,6 +763,18 @@ static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, + value |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH; + + writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); ++ ++ deb_verbose("(2): exiting -- value is %d, base is %p\n", value, (void *)base); ++} ++ ++void tegra186_gpio_set_by_name(const char *name, unsigned int offset, ++ int level) ++{ ++ struct gpio_chip *chip = find_chip_by_name(name); ++ if (chip) { tegra186_gpio_set(chip, offset, level); } ++ else { ++ pr_err("GPIO cannot find chip by name, %s\n", name); ++ } + } + + static int tegra186_gpio_set_config(struct gpio_chip *chip, +@@ -689,6 +785,8 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, + u32 debounce, value; + void __iomem *base; + ++ deb_verbose("GPIO, chip %s, offset %u\n", chip->label, offset); ++ + base = tegra186_gpio_get_base(gpio, offset); + if (base == NULL) + return -ENXIO; +@@ -725,6 +823,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) + unsigned int i, j; + int err; + ++ deb_verbose("GPIO chip %s\n", chip->label); ++ + if (!gpio->soc->pinmux || gpio->soc->num_pin_ranges == 0) + return 0; + +@@ -768,6 +868,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, + struct tegra_gpio *gpio = gpiochip_get_data(chip); + unsigned int port, pin, i, offset = 0; + ++ deb_verbose("GPIO chip %s\n", chip->label); ++ + if (WARN_ON(chip->of_gpio_n_cells < 2)) + return -EINVAL; + +@@ -1037,6 +1139,8 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) + unsigned int i, j; + u32 value; + ++ // deb_verbose("GPIO, chip \n", gpio->gpio.label); ++ + for (i = 0; i < gpio->soc->num_ports; i++) { + const struct tegra_gpio_port *port = &gpio->soc->ports[i]; + unsigned int offset, p = port->port; +@@ -1107,6 +1211,177 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) + return -EINVAL; + } + ++#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++ ++ // functoons that are passed through. Function body is in gpio-guest-proxy.c ++ extern int gpiochip_generic_request_redirect(struct gpio_chip *gc, unsigned offset); ++ ++ extern void gpiochip_generic_free_redirect(struct gpio_chip *gc, unsigned offset); ++ ++ extern int tegra186_gpio_get_direction_redirect(struct gpio_chip *chip, ++ unsigned int offset); ++ ++ extern int tegra186_gpio_direction_input_redirect(struct gpio_chip *chip, ++ unsigned int offset); ++ ++ extern int tegra186_gpio_direction_output_redirect(struct gpio_chip *chip, ++ unsigned int offset, int level); ++ ++ extern int tegra186_gpio_get_redirect(struct gpio_chip *chip, unsigned int offset); ++ ++ extern void tegra186_gpio_set_redirect(struct gpio_chip *chip, unsigned int offset, ++ int level); ++ ++ extern void tegra186_gpio_set_by_name_redirect(const char *name, unsigned int offset, ++ int level); ++ ++ extern int tegra186_gpio_set_config_redirect(struct gpio_chip *chip, ++ unsigned int offset, ++ unsigned long config); ++ ++ extern int tegra186_gpio_set_config_redirect(struct gpio_chip *chip, ++ unsigned int offset, ++ unsigned long config); ++ ++ extern int tegra_gpio_timestamp_control_redirect(struct gpio_chip *chip, unsigned offset, ++ int enable); ++ ++ extern int tegra_gpio_timestamp_read_redirect(struct gpio_chip *chip, unsigned offset, ++ u64 *ts); ++ ++ extern int tegra_gpio_suspend_configure_redirect(struct gpio_chip *chip, unsigned offset, ++ enum gpiod_flags dflags); ++ ++ extern int tegra186_gpio_add_pin_ranges_redirect(struct gpio_chip *chip); ++ ++ inline void gpio_hook(struct tegra_gpio *gpio) { ++ gpio->gpio.request = gpiochip_generic_request_redirect; ++ gpio->gpio.free = gpiochip_generic_free_redirect; ++ gpio->gpio.get_direction = tegra186_gpio_get_direction_redirect; ++ gpio->gpio.direction_input = tegra186_gpio_direction_input_redirect; ++ gpio->gpio.direction_output = tegra186_gpio_direction_output_redirect; ++ gpio->gpio.get = tegra186_gpio_get_redirect; ++ gpio->gpio.set = tegra186_gpio_set_redirect; ++ gpio->gpio.set_config = tegra186_gpio_set_config_redirect; ++ gpio->gpio.timestamp_control = tegra_gpio_timestamp_control_redirect; ++ gpio->gpio.timestamp_read = tegra_gpio_timestamp_read_redirect; ++ gpio->gpio.suspend_configure = tegra_gpio_suspend_configure_redirect; ++ gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges_redirect; ++ gpio->gpio.base = -1; ++ } ++ ++ inline void gpio_unhook(struct tegra_gpio *gpio) { ++ gpio->gpio.request = gpiochip_generic_request; ++ gpio->gpio.free = gpiochip_generic_free; ++ gpio->gpio.get_direction = tegra186_gpio_get_direction; ++ gpio->gpio.direction_input = tegra186_gpio_direction_input; ++ gpio->gpio.direction_output = tegra186_gpio_direction_output; ++ gpio->gpio.get = tegra186_gpio_get; ++ gpio->gpio.set = tegra186_gpio_set; ++ gpio->gpio.set_config = tegra186_gpio_set_config; ++ gpio->gpio.timestamp_control = tegra_gpio_timestamp_control; ++ gpio->gpio.timestamp_read = tegra_gpio_timestamp_read; ++ gpio->gpio.suspend_configure = tegra_gpio_suspend_configure; ++ gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; ++ gpio->gpio.base = -1; ++ } ++ ++ extern int tegra_gpio_guest_init(struct gpio_chip *gpio); ++ ++ #define MAX_CHIP 2 // check this value against value in gpio_host-proxy.h ++ ++ static int gpio_chip_count = 0; ++ struct tegra_gpio *tegra_gpio_hosts[MAX_CHIP] = {NULL, NULL}; ++ atomic_t tegra_gpio_hosts_ready = ATOMIC_INIT(0); ++ ++ /* note for reference: ++ struct tegra_gpio { ++ struct gpio_chip gpio; ++ struct irq_chip intc; ++ unsigned int num_irq; ++ unsigned int *irq; ++ [...] ++ } ++ ++ struct gpio_chip { ++ const char *label; ++ struct gpio_device *gpiodev; ++ struct device *parent; ++ struct module *owner; ++ [...] ++ } ++ ++ struct gpio_device *gpiodev { ++ int id; ++ struct device dev; ++ } ++ ++ */ ++ ++ /* preserve_tegrachip() and unpreserve_all_tegrachips() functions ++ * ++ * store the tegra_gpio and gpio_chip pointers for direct use by proxy drivers ++ * the preserve functionsalso assist in allocation and deallocaton, setting up and unseting ++ * of proxy related data */ ++ static void preserve_tegrachip(struct tegra_gpio *tegrachip) { ++ struct gpio_chip *gpiochip = &tegrachip->gpio; ++ int id = gpiochip->gpiodev->id; ++ if( id != gpio_chip_count) { ++ // we assume gpiochip0 will be registered in slot 0 and gpiochip1 in slot 1 ++ // if this nonfatal error triggers, we register using 'id' as an index ansyhow ++ deb_debug("gpio device id mismatch, gpio_chip_count = %d, id = %d\n", gpio_chip_count, id); ++ } ++ if (gpio_chip_count >= MAX_CHIP) { ++ pr_err("GPIO, *ERROR* maximum chip count is exceeded (%d)", gpio_chip_count); ++ } ++ else { ++ tegra_gpio_hosts[id] = tegrachip; ++ deb_debug("put chip %s in list for passthrough in slot %d [0..1]\n", gpiochip->label, id); ++ }; ++ atomic_set(&tegra_gpio_hosts_ready, ++gpio_chip_count); ++ } ++ ++ ++ /* deactivates tegra_gpio_hosts array, function is called by guest proxy. ++ * allocations and hooks are not needed if guest proxy driver unloads ++ * (paranoia because drivers are built in) */ ++ void unpreserve_all_tegrachips(void) { ++ struct tegra_gpio ** tegrachip; ++ int i = 0; ++ ++ // wait until tegra driver has set the tegra_gpio_hosts array -- this seems a bit paranoid ++ while (atomic_read(&tegra_gpio_hosts_ready) != MAX_CHIP) { ++ msleep(100); // Sleep briefly instead of looping infinitely. ++ if( i++ > 120 ) { ++ pr_err("could not access tegra_gpio chip array\n"); ++ return; ++ } ++ } ++ for( i = 0 ; i < MAX_CHIP ; i++){ ++ tegrachip = &tegra_gpio_hosts[i]; ++ if(*tegrachip) ++ gpio_unhook(*tegrachip); ++ *tegrachip = NULL; ++ } ++ } ++ EXPORT_SYMBOL_GPL(unpreserve_all_tegrachips); ++ ++ /* find_chip_by_id ++ * replacement for find_chip_by_name, because it is slightly faster */ ++ inline struct gpio_chip * find_chip_by_id(int id) { ++ int i = 0; ++ while (atomic_read(&tegra_gpio_hosts_ready) != MAX_CHIP) { ++ msleep(100); // Sleep briefly instead of looping infinitely. ++ if( i++ > 120 ) { ++ pr_err("tegra_gpio_hosts setup error\n"); ++ return NULL; ++ } ++ } ++ return &tegra_gpio_hosts[id]->gpio; ++ } ++ EXPORT_SYMBOL_GPL(find_chip_by_id); ++#endif ++ + static int tegra186_gpio_probe(struct platform_device *pdev) + { + unsigned int i, j, offset; +@@ -1120,17 +1395,57 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + int value; + void __iomem *base; + ++ #if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++ bool guest_proxy = false; ++ #endif ++ ++ deb_debug("\n"); ++ + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); +- if (!gpio) ++ if (!gpio) { ++ pr_err("GPIO devm_kzalloc error"); + return -ENOMEM; ++ } + + gpio->soc = of_device_get_match_data(&pdev->dev); + gpio->gpio.label = gpio->soc->name; + gpio->gpio.parent = &pdev->dev; + ++ #if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++ ++ deb_debug("GPIO Proxy code\n"); ++ ++ // If virtual-pa node is defined, it means that we are using a virtual GPIO ++ // then we have to initialise the gpio-guest ++ err = of_property_read_u64(pdev->dev.of_node, "virtual-pa", &gpio_vpa); ++ if(!err){ ++ // code executed in gpio-guest only ++ deb_info("GPIO virtual-pa: 0x%llx\n", gpio_vpa); ++ /* we are now running gpio-guest-proxy code */ ++ guest_proxy = true; ++ ret = tegra_gpio_guest_init(&gpio->gpio); ++ gpio_hook(gpio); ++ // unpreserve_all_tegrachips() will unhook functions, if it ever was called ++ ++ // we need to handle irq? ++ ++ // is the assumption on next comment line not valid for gpio? (because of interrupts) ++ // in guest proxy driver subsequent code is redundant -- thus return ++ return ret; ++ } ++ // we assune that guest proxy code will not execute here ++ // that is maybe a false assumption that further setup is unecessary ++ // or maybe not, we need to register gpio with pdev for the passthough functions ++ BUG_ON(gpio_vpa != 0); ++ ++ #endif ++ + gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security"); +- if (IS_ERR(gpio->secure)) ++ if (IS_ERR(gpio->secure)) { ++ pr_err("GPIO *ERROR* devm_platform_ioremap_resource_byname pdev->name = \"%s\"", pdev->name); + return PTR_ERR(gpio->secure); ++ } ++ + + /* count the number of banks in the controller */ + for (i = 0; i < gpio->soc->num_ports; i++) +@@ -1194,21 +1509,31 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + gpio->irq[i] = err; + } + +- +- gpio->gpio.request = gpiochip_generic_request; +- gpio->gpio.free = gpiochip_generic_free; +- gpio->gpio.get_direction = tegra186_gpio_get_direction; +- gpio->gpio.direction_input = tegra186_gpio_direction_input; +- gpio->gpio.direction_output = tegra186_gpio_direction_output; +- gpio->gpio.get = tegra186_gpio_get, +- gpio->gpio.set = tegra186_gpio_set; +- gpio->gpio.set_config = tegra186_gpio_set_config; +- gpio->gpio.timestamp_control = tegra_gpio_timestamp_control; +- gpio->gpio.timestamp_read = tegra_gpio_timestamp_read; +- gpio->gpio.suspend_configure = tegra_gpio_suspend_configure; +- gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; +- +- gpio->gpio.base = -1; ++ #ifdef CONFIG_TEGRA_GPIO_HOST_PROXY ++ // guest proxy guard just in case we execute this section later as guest (we should not) ++ if(!guest_proxy) { ++ #endif ++ // gpio_unhook is the same as these standard settings ++ // these pointers are host only ++ gpio->gpio.request = gpiochip_generic_request; ++ gpio->gpio.free = gpiochip_generic_free; ++ gpio->gpio.get_direction = tegra186_gpio_get_direction; ++ gpio->gpio.direction_input = tegra186_gpio_direction_input; ++ gpio->gpio.direction_output = tegra186_gpio_direction_output; ++ gpio->gpio.get = tegra186_gpio_get; ++ gpio->gpio.set = tegra186_gpio_set; ++ gpio->gpio.set_config = tegra186_gpio_set_config; ++ gpio->gpio.timestamp_control = tegra_gpio_timestamp_control; ++ gpio->gpio.timestamp_read = tegra_gpio_timestamp_read; ++ gpio->gpio.suspend_configure = tegra_gpio_suspend_configure; ++ gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; ++ gpio->gpio.base = -1; ++ #ifdef CONFIG_TEGRA_GPIO_HOST_PROXY ++ } ++ else { ++ deb_info("guest driver found executing host code"); ++ } ++ #endif + + for (i = 0; i < gpio->soc->num_ports; i++) + gpio->gpio.ngpio += gpio->soc->ports[i].pins; +@@ -1229,6 +1554,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + return -ENOMEM; + + names[offset + j] = name; ++ // deb_verbose("GPIO, name=\n", name); + } + + offset += port->pins; +@@ -1343,10 +1669,46 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + if (gpio->use_timestamp) + tegra_gte_setup(gpio); + ++ #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) ++ ++ deb_debug("GPIO, initialised gpio label=%s\n", gpio->gpio.label); ++ deb_debug("GPIO, initialised gpio at %p\n", gpio); ++ deb_debug("GPIO, initialised gpio->secure at %p\n", gpio->secure); ++ deb_debug("GPIO, initialised gpio->base at %p\n", gpio->base); ++ deb_debug("GPIO, initialised gpio->gte_regs at %p\n", gpio->gte_regs); ++ ++ preserve_tegrachip(gpio); ++ ++ /* this section is from copydriver branch -- not valid here ++ ++ // these ifdefs do not define host and guest kernel module code ++ // but common code in the stock 'tegra186-gpio' -- it is compiled if module is set in .config ++ ++ // we actually have two gpio chips -- this probe function will be called twice. ++ // copy set value to ready export ++ ++ if ( ! strcmp(gpio->gpio.label,"tegra234-gpio") ) { ++ gpio_ready += 1; ++ } ++ else if ( ! strcmp(gpio->gpio.label,"tegra234-gpio-aon") ) { ++ gpio_ready += 1; ++ } ++ else ++ pr_err("Can't match gpio chip label, in %s"); ++ if ( gpio_ready > 2 ) ++ pr_err("Found too many chips, in %s"); ++ ++ memcpy(&preset_gpio_local[n], gpio, sizeof(struct tegra_gpio)); ++ if ( gpio_ready == 2 ) ++ complete(&gpio_data_ready); ++ ++ deb_debug("GPIO preset_gpio %s exported in \n", gpio->gpio.label); ++ */ ++ #endif + return 0; + } + +-#ifdef CONFIG_PM_SLEEP ++ #ifdef CONFIG_PM_SLEEP + static int tegra_gpio_resume_early(struct device *dev) + { + struct tegra_gpio *gpio = dev_get_drvdata(dev); +@@ -1723,7 +2085,9 @@ static struct platform_driver tegra186_gpio_driver = { + .probe = tegra186_gpio_probe, + .remove = tegra186_gpio_remove, + }; +-module_platform_driver(tegra186_gpio_driver); ++ ++// module_platform_driver(tegra186_gpio_driver); ++builtin_platform_driver(tegra186_gpio_driver); + + MODULE_DESCRIPTION("NVIDIA Tegra186 GPIO controller driver"); + MODULE_AUTHOR("Thierry Reding "); +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index 2613881a66e6..7c00c935c69f 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -83,6 +83,16 @@ struct linehandle_state { + GPIOHANDLE_REQUEST_OPEN_DRAIN | \ + GPIOHANDLE_REQUEST_OPEN_SOURCE) + ++// #define GPIO_DEBUG ++ ++#ifdef GPIO_DEBUG ++#define deb_info(fmt, ...) printk(KERN_INFO"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_debug(fmt, ...) printk(KERN_DEBUG"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#else ++#define deb_info(fmt, ...) ++#define deb_debug(fmt, ...) ++#endif ++ + static int linehandle_validate_flags(u32 flags) + { + /* Return an error if an unknown flag is set */ +@@ -155,6 +165,8 @@ static long linehandle_set_config(struct linehandle_state *lh, + int i, ret; + u32 lflags; + ++ deb_debug("\n"); ++ + if (copy_from_user(&gcnf, ip, sizeof(gcnf))) + return -EFAULT; + +@@ -199,6 +211,8 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd, + DECLARE_BITMAP(vals, GPIOHANDLES_MAX); + int i; + ++ deb_debug("cmd=0x%x, user_pointer=0x%p\n", cmd, ip); ++ + if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { + /* NOTE: It's ok to read values of output lines. */ + int ret = gpiod_get_array_value_complex(false, +@@ -268,6 +282,8 @@ static void linehandle_free(struct linehandle_state *lh) + + static int linehandle_release(struct inode *inode, struct file *file) + { ++ deb_debug("\n"); ++ + linehandle_free(file->private_data); + return 0; + } +@@ -290,6 +306,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) + int fd, i, ret; + u32 lflags; + ++ deb_debug("\n"); ++ + if (copy_from_user(&handlereq, ip, sizeof(handlereq))) + return -EFAULT; + if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX)) +@@ -1040,6 +1058,8 @@ static long linereq_set_values_unlocked(struct linereq *lr, + unsigned int i, didx, num_set; + int ret; + ++ deb_debug("\n"); ++ + bitmap_zero(vals, GPIO_V2_LINES_MAX); + for (num_set = 0, i = 0; i < lr->num_lines; i++) { + if (lv->mask & BIT_ULL(i)) { +@@ -1079,6 +1099,8 @@ static long linereq_set_values(struct linereq *lr, void __user *ip) + struct gpio_v2_line_values lv; + int ret; + ++ deb_debug("\n"); ++ + if (copy_from_user(&lv, ip, sizeof(lv))) + return -EFAULT; + +@@ -1165,6 +1187,8 @@ static long linereq_ioctl(struct file *file, unsigned int cmd, + struct linereq *lr = file->private_data; + void __user *ip = (void __user *)arg; + ++ deb_debug("\n"); ++ + if (cmd == GPIO_V2_LINE_GET_VALUES_IOCTL) + return linereq_get_values(lr, ip); + else if (cmd == GPIO_V2_LINE_SET_VALUES_IOCTL) +@@ -1296,6 +1320,8 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) + u64 flags; + unsigned int i; + int fd, ret; ++ ++ deb_debug("\n"); + + if (copy_from_user(&ulr, ip, sizeof(ulr))) + return -EFAULT; +@@ -1702,6 +1728,8 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) + int ret; + int irq, irqflags = 0; + ++ deb_debug("\n"); ++ + if (copy_from_user(&eventreq, ip, sizeof(eventreq))) + return -EFAULT; + +@@ -1870,7 +1898,6 @@ static void gpio_v2_line_info_changed_to_v1( + lic_v1->timestamp = lic_v2->timestamp_ns; + lic_v1->event_type = lic_v2->event_type; + } +- + #endif /* CONFIG_GPIO_CDEV_V1 */ + + static void gpio_desc_to_lineinfo(struct gpio_desc *desc, +@@ -2000,6 +2027,8 @@ static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip, + struct gpioline_info lineinfo; + struct gpio_v2_line_info lineinfo_v2; + ++ deb_debug("\n"); ++ + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + +@@ -2035,6 +2064,8 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip, + struct gpio_desc *desc; + struct gpio_v2_line_info lineinfo; + ++ deb_debug("\n"); ++ + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + +@@ -2089,6 +2120,8 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + struct gpio_device *gdev = cdev->gdev; + void __user *ip = (void __user *)arg; + ++ deb_debug("cmd=0x%x, user_pointer=0x%p\n", cmd, ip); ++ + /* We fail any subsequent ioctl():s when the chip is gone */ + if (!gdev->chip) + return -ENODEV; +@@ -2122,6 +2155,8 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + static long gpio_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg) + { ++ deb_debug("cmd=%d\n", cmd); ++ + return gpio_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); + } + #endif +@@ -2216,8 +2251,8 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, + if (count < event_size) { + spin_unlock(&cdev->wait.lock); + return -EINVAL; +- } + #endif ++ } + ret = kfifo_out(&cdev->events, &event, 1); + spin_unlock(&cdev->wait.lock); + if (ret != 1) { +@@ -2261,6 +2296,8 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) + struct gpio_chardev_data *cdev; + int ret = -ENOMEM; + ++ deb_debug("\n"); ++ + /* Fail on open if the backing gpiochip is gone */ + if (!gdev->chip) + return -ENODEV; +@@ -2285,7 +2322,6 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) + + get_device(&gdev->dev); + file->private_data = cdev; +- + ret = nonseekable_open(inode, file); + if (ret) + goto out_unregister_notifier; +@@ -2313,6 +2349,8 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file) + struct gpio_chardev_data *cdev = file->private_data; + struct gpio_device *gdev = cdev->gdev; + ++ deb_debug("\n"); ++ + bitmap_free(cdev->watched_lines); + blocking_notifier_chain_unregister(&gdev->notifier, + &cdev->lineinfo_changed_nb); +@@ -2339,6 +2377,8 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) + { + int ret; + ++ deb_debug("\n"); ++ + cdev_init(&gdev->chrdev, &gpio_fileops); + gdev->chrdev.owner = THIS_MODULE; + gdev->dev.devt = MKDEV(MAJOR(devt), gdev->id); +@@ -2355,5 +2395,7 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) + + void gpiolib_cdev_unregister(struct gpio_device *gdev) + { ++ deb_debug("\n"); ++ + cdev_device_del(&gdev->chrdev, &gdev->dev); + } +diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c +index 30e2476a6dc4..873cd7ff1753 100644 +--- a/drivers/gpio/gpiolib-legacy.c ++++ b/drivers/gpio/gpiolib-legacy.c +@@ -6,8 +6,21 @@ + + #include "gpiolib.h" + ++// #define GPIO_DEBUG ++ ++#ifdef GPIO_DEBUG ++#define deb_info(fmt, ...) printk(KERN_INFO"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_debug(fmt, ...) printk(KERN_DEBUG"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#else ++#define deb_info(fmt, ...) ++#define deb_debug(fmt, ...) ++#endif ++ ++ + void gpio_free(unsigned gpio) + { ++ deb_debug("\n"); ++ + gpiod_free(gpio_to_desc(gpio)); + } + EXPORT_SYMBOL_GPL(gpio_free); +@@ -23,6 +36,8 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) + struct gpio_desc *desc; + int err; + ++ deb_debug("label=%s\n", label); ++ + desc = gpio_to_desc(gpio); + + /* Compatibility: assume unavailable "valid" GPIOs will appear later */ +@@ -69,6 +84,8 @@ int gpio_request(unsigned gpio, const char *label) + { + struct gpio_desc *desc = gpio_to_desc(gpio); + ++ deb_debug("label=%s\n", label); ++ + /* Compatibility: assume unavailable "valid" GPIOs will appear later */ + if (!desc && gpio_is_valid(gpio)) + return -EPROBE_DEFER; +@@ -86,6 +103,8 @@ int gpio_request_array(const struct gpio *array, size_t num) + { + int i, err; + ++ deb_debug("\n"); ++ + for (i = 0; i < num; i++, array++) { + err = gpio_request_one(array->gpio, array->flags, array->label); + if (err) +diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c +index 3ef71ca242ba..0aaefbe3f6d9 100644 +--- a/drivers/gpio/gpiolib-sysfs.c ++++ b/drivers/gpio/gpiolib-sysfs.c +@@ -18,6 +18,16 @@ + #define GPIO_IRQF_TRIGGER_BOTH (GPIO_IRQF_TRIGGER_FALLING | \ + GPIO_IRQF_TRIGGER_RISING) + ++// #define GPIO_DEBUG ++ ++#ifdef GPIO_DEBUG ++#define deb_info(fmt, ...) printk(KERN_INFO"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_debug(fmt, ...) printk(KERN_DEBUG"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#else ++#define deb_info(fmt, ...) ++#define deb_debug(fmt, ...) ++#endif ++ + struct gpiod_data { + struct gpio_desc *desc; + +@@ -489,6 +499,8 @@ static ssize_t export_store(struct class *class, + struct gpio_chip *gc; + int offset; + ++ deb_debug("\n"); ++ + status = kstrtol(buf, 0, &gpio); + + /* If buf is not a number then try to find by name */ +diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c +index 50abb1c20df0..bdd7f6310dce 100644 +--- a/drivers/gpio/gpiolib.c ++++ b/drivers/gpio/gpiolib.c +@@ -31,6 +31,16 @@ + #define CREATE_TRACE_POINTS + #include + ++// #define GPIO_DEBUG ++ ++#ifdef GPIO_DEBUG ++#define deb_info(fmt, ...) printk(KERN_INFO"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_debug(fmt, ...) printk(KERN_DEBUG"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#else ++#define deb_info(fmt, ...) ++#define deb_debug(fmt, ...) ++#endif ++ + /* Implementation infrastructure for GPIO interfaces. + * + * The GPIO programming interface allows for inlining speed-critical +@@ -45,11 +55,11 @@ + * + * Otherwise, minimize overhead in what may be bitbanging codepaths. + */ +-#ifdef DEBUG ++ #ifdef DEBUG + #define extra_checks 1 + #else + #define extra_checks 0 +-#endif ++ #endif + + /* Device and char device-related information */ + static DEFINE_IDA(gpio_ida); +@@ -105,6 +115,8 @@ struct gpio_desc *gpio_to_desc(unsigned gpio) + { + struct gpio_device *gdev; + unsigned long flags; ++ ++ deb_debug("\n"); + + spin_lock_irqsave(&gpio_lock, flags); + +@@ -140,6 +152,8 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, + { + struct gpio_device *gdev = gc->gpiodev; + ++ deb_debug("HW Number %u\n", hwnum); ++ + if (hwnum >= gdev->ngpio) + return ERR_PTR(-EINVAL); + +@@ -170,6 +184,8 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); + */ + struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) + { ++ deb_debug("\n"); ++ + if (!desc || !desc->gdev) + return NULL; + return desc->gdev->chip; +@@ -214,6 +230,8 @@ int gpiod_get_direction(struct gpio_desc *desc) + unsigned offset; + int ret; + ++ deb_debug("name=%s, label=%s\n", desc->name, desc->label); ++ + gc = gpiod_to_chip(desc); + offset = gpio_chip_hwgpio(desc); + +@@ -253,6 +271,8 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) + { + struct gpio_device *prev, *next; + ++ deb_debug("\n"); ++ + if (list_empty(&gpio_devices)) { + /* initial entry in list */ + list_add_tail(&gdev->list, &gpio_devices); +@@ -301,6 +321,8 @@ struct gpio_desc *gpio_name_to_desc(const char * const name) + struct gpio_device *gdev; + unsigned long flags; + ++ // deb_debug("\n"); ++ + if (!name) + return NULL; + +@@ -340,6 +362,8 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) + struct gpio_device *gdev = gc->gpiodev; + int i; + ++ deb_debug("\n"); ++ + /* First check all names if they are unique */ + for (i = 0; i != gc->ngpio; ++i) { + struct gpio_desc *gpio; +@@ -375,6 +399,8 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip) + int ret, i; + int count; + ++ deb_debug("\n"); ++ + count = fwnode_property_string_array_count(fwnode, "gpio-line-names"); + if (count < 0) + return 0; +@@ -409,6 +435,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) + { + unsigned long *p; + ++ deb_debug("\n"); ++ + p = bitmap_alloc(gc->ngpio, GFP_KERNEL); + if (!p) + return NULL; +@@ -421,6 +449,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) + + static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) + { ++ deb_debug("\n"); ++ + if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask)) + return 0; + +@@ -433,6 +463,8 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) + + static int gpiochip_init_valid_mask(struct gpio_chip *gc) + { ++ deb_debug("\n"); ++ + if (gc->init_valid_mask) + return gc->init_valid_mask(gc, + gc->valid_mask, +@@ -443,12 +475,16 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) + + static void gpiochip_free_valid_mask(struct gpio_chip *gc) + { ++ deb_debug("\n"); ++ + bitmap_free(gc->valid_mask); + gc->valid_mask = NULL; + } + + static int gpiochip_add_pin_ranges(struct gpio_chip *gc) + { ++ deb_debug("\n"); ++ + if (gc->add_pin_ranges) + return gc->add_pin_ranges(gc); + +@@ -458,6 +494,8 @@ static int gpiochip_add_pin_ranges(struct gpio_chip *gc) + bool gpiochip_line_is_valid(const struct gpio_chip *gc, + unsigned int offset) + { ++ deb_debug("chip %s, offset=%u\n", gc->label, offset); ++ + /* No mask means all valid */ + if (likely(!gc->valid_mask)) + return true; +@@ -470,6 +508,8 @@ static void gpiodevice_release(struct device *dev) + struct gpio_device *gdev = dev_get_drvdata(dev); + unsigned long flags; + ++ deb_debug("\n"); ++ + spin_lock_irqsave(&gpio_lock, flags); + list_del(&gdev->list); + spin_unlock_irqrestore(&gpio_lock, flags); +@@ -480,7 +520,7 @@ static void gpiodevice_release(struct device *dev) + kfree(gdev); + } + +-#ifdef CONFIG_GPIO_CDEV ++ #ifdef CONFIG_GPIO_CDEV + #define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt)) + #define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev)) + #else +@@ -490,13 +530,27 @@ static void gpiodevice_release(struct device *dev) + */ + #define gcdev_register(gdev, devt) device_add(&(gdev)->dev) + #define gcdev_unregister(gdev) device_del(&(gdev)->dev) +-#endif ++ #endif ++ ++static int gpio_dev_count = 0; ++struct gpio_device *proxy_host_gpio_dev[2] = {NULL, NULL}; ++EXPORT_SYMBOL_GPL(proxy_host_gpio_dev); + + static int gpiochip_setup_dev(struct gpio_device *gdev) + { + int ret; + ++ deb_debug("\n"); ++ ++ // store GPIO char device for use by proxy host driver (In guest this is redundant) ++ if (gpio_dev_count == 2) { ++ pr_err("GPIO %s, error, found more than two devices -- file %s", __func__, __FILE__); ++ } ++ proxy_host_gpio_dev[gpio_dev_count++] = gdev; ++ // we continue to populate gdev ++ + ret = gcdev_register(gdev, gpio_devt); ++ + if (ret) + return ret; + +@@ -522,6 +576,8 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) + struct gpio_desc *desc; + int rv; + ++ deb_debug("\n"); ++ + desc = gpiochip_get_desc(gc, hog->chip_hwnum); + if (IS_ERR(desc)) { + chip_err(gc, "%s: unable to get GPIO desc: %ld\n", __func__, +@@ -542,6 +598,8 @@ static void machine_gpiochip_add(struct gpio_chip *gc) + { + struct gpiod_hog *hog; + ++ deb_debug("\n"); ++ + mutex_lock(&gpio_machine_hogs_mutex); + + list_for_each_entry(hog, &gpio_machine_hogs, list) { +@@ -557,6 +615,8 @@ static void gpiochip_setup_devs(void) + struct gpio_device *gdev; + int ret; + ++ deb_debug("\n"); ++ + list_for_each_entry(gdev, &gpio_devices, list) { + ret = gpiochip_setup_dev(gdev); + if (ret) +@@ -576,6 +636,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, + int base = gc->base; + struct gpio_device *gdev; + ++ deb_debug("\n"); ++ + /* + * First: allocate and populate the internal stat container, and + * set up the struct device. +@@ -591,13 +653,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, + gdev->dev.of_node = gc->parent->of_node; + } + +-#ifdef CONFIG_OF_GPIO ++ #ifdef CONFIG_OF_GPIO + /* If the gpiochip has an assigned OF node this takes precedence */ + if (gc->of_node) + gdev->dev.of_node = gc->of_node; + else + gc->of_node = gdev->dev.of_node; +-#endif ++ #endif + + /* + * Assign fwnode depending on the result of the previous calls, +@@ -689,9 +751,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, + + BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier); + +-#ifdef CONFIG_PINCTRL ++ #ifdef CONFIG_PINCTRL + INIT_LIST_HEAD(&gdev->pin_ranges); +-#endif ++ #endif + + if (gc->names) + ret = gpiochip_set_desc_names(gc); +@@ -802,6 +864,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); + */ + void *gpiochip_get_data(struct gpio_chip *gc) + { ++ deb_debug("\n"); ++ + return gc->gpiodev->data; + } + EXPORT_SYMBOL_GPL(gpiochip_get_data); +@@ -818,6 +882,8 @@ void gpiochip_remove(struct gpio_chip *gc) + unsigned long flags; + unsigned int i; + ++ deb_debug("\n"); ++ + /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ + gpiochip_sysfs_unregister(gdev); + gpiochip_free_hogs(gc); +@@ -875,6 +941,8 @@ struct gpio_chip *gpiochip_find(void *data, + struct gpio_chip *gc = NULL; + unsigned long flags; + ++ deb_debug("\n"); ++ + spin_lock_irqsave(&gpio_lock, flags); + list_for_each_entry(gdev, &gpio_devices, list) + if (gdev->chip && match(gdev->chip, data)) { +@@ -895,12 +963,15 @@ static int gpiochip_match_name(struct gpio_chip *gc, void *data) + return !strcmp(gc->label, name); + } + +-static struct gpio_chip *find_chip_by_name(const char *name) ++struct gpio_chip *find_chip_by_name(const char *name) + { ++ deb_debug("\n"); ++ + return gpiochip_find((void *)name, gpiochip_match_name); + } ++EXPORT_SYMBOL_GPL(find_chip_by_name); + +-#ifdef CONFIG_GPIOLIB_IRQCHIP ++ #ifdef CONFIG_GPIOLIB_IRQCHIP + + /* + * The following is irqchip helper code for gpiochips. +@@ -910,6 +981,8 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc) + { + struct gpio_irq_chip *girq = &gc->irq; + ++ deb_debug("\n"); ++ + if (!girq->init_hw) + return 0; + +@@ -920,6 +993,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) + { + struct gpio_irq_chip *girq = &gc->irq; + ++ deb_debug("\n"); ++ + if (!girq->init_valid_mask) + return 0; + +@@ -934,6 +1009,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) + + static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) + { ++ deb_debug("\n"); ++ + bitmap_free(gc->irq.valid_mask); + gc->irq.valid_mask = NULL; + } +@@ -941,6 +1018,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) + bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, + unsigned int offset) + { ++ deb_debug("\n"); ++ + if (!gpiochip_line_is_valid(gc, offset)) + return false; + /* No mask means all valid */ +@@ -966,6 +1045,8 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, + struct gpio_irq_chip *girq = &gc->irq; + struct device *dev = &gc->gpiodev->dev; + ++ deb_debug("\n"); ++ + if (!girq->domain) { + chip_err(gc, "called %s before setting up irqchip\n", + __func__); +@@ -1011,7 +1092,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gc, + } + EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); + +-#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY ++ #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + + /** + * gpiochip_set_hierarchical_irqchip() - connects a hierarchical irqchip +@@ -1250,6 +1331,8 @@ void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, + { + struct irq_fwspec *fwspec; + ++ deb_debug("\n"); ++ + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; +@@ -1269,6 +1352,8 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, + { + struct irq_fwspec *fwspec; + ++ deb_debug("\n"); ++ + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; +@@ -1296,7 +1381,7 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) + return false; + } + +-#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ ++ #endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ + + /** + * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip +@@ -1314,6 +1399,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, + struct gpio_chip *gc = d->host_data; + int ret = 0; + ++ deb_debug("\n"); ++ + if (!gpiochip_irqchip_irq_valid(gc, hwirq)) + return -ENXIO; + +@@ -1415,7 +1502,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) + if (!gpiochip_irqchip_irq_valid(gc, offset)) + return -ENXIO; + +-#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY ++ #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + if (irq_domain_is_hierarchy(domain)) { + struct irq_fwspec spec; + +@@ -1426,7 +1513,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) + + return irq_create_fwspec_mapping(&spec); + } +-#endif ++ #endif + + return irq_create_mapping(domain, offset); + } +@@ -1709,7 +1796,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, + } + gc->irq.threaded = threaded; + of_node = gc->parent->of_node; +-#ifdef CONFIG_OF_GPIO ++ #ifdef CONFIG_OF_GPIO + /* + * If the gpiochip has an assigned OF node this takes precedence + * FIXME: get rid of this and use gc->parent->of_node +@@ -1717,7 +1804,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, + */ + if (gc->of_node) + of_node = gc->of_node; +-#endif ++ #endif + /* + * Specifying a default trigger is a terrible idea if DT or ACPI is + * used to configure the interrupts, as you may end-up with +@@ -1796,7 +1883,7 @@ static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) + static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) + { } + +-#endif /* CONFIG_GPIOLIB_IRQCHIP */ ++ #endif /* CONFIG_GPIOLIB_IRQCHIP */ + + /** + * gpiochip_generic_request() - request the gpio function for a pin +@@ -1805,10 +1892,12 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) + */ + int gpiochip_generic_request(struct gpio_chip *gc, unsigned offset) + { +-#ifdef CONFIG_PINCTRL ++ deb_debug("chip %s, offset=%u\n", gc->label, offset); ++ ++ #ifdef CONFIG_PINCTRL + if (list_empty(&gc->gpiodev->pin_ranges)) + return 0; +-#endif ++ #endif + + return pinctrl_gpio_request(gc->gpiodev->base + offset); + } +@@ -1821,10 +1910,12 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); + */ + void gpiochip_generic_free(struct gpio_chip *gc, unsigned offset) + { +-#ifdef CONFIG_PINCTRL ++ deb_debug("GPIO %s, chip %s, offset=%u\n", gc->label, offset); ++ ++ #ifdef CONFIG_PINCTRL + if (list_empty(&gc->gpiodev->pin_ranges)) + return; +-#endif ++ #endif + + pinctrl_gpio_free(gc->gpiodev->base + offset); + } +@@ -1839,11 +1930,13 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free); + int gpiochip_generic_config(struct gpio_chip *gc, unsigned offset, + unsigned long config) + { ++ deb_debug("\n"); ++ + return pinctrl_gpio_set_config(gc->gpiodev->base + offset, config); + } + EXPORT_SYMBOL_GPL(gpiochip_generic_config); + +-#ifdef CONFIG_PINCTRL ++ #ifdef CONFIG_PINCTRL + + /** + * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping +@@ -1865,6 +1958,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, + struct gpio_device *gdev = gc->gpiodev; + int ret; + ++ deb_debug("\n"); ++ + pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); + if (!pin_range) { + chip_err(gc, "failed to allocate pin ranges\n"); +@@ -1923,6 +2018,8 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, + struct gpio_device *gdev = gc->gpiodev; + int ret; + ++ deb_debug("\n"); ++ + pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); + if (!pin_range) { + chip_err(gc, "failed to allocate pin ranges\n"); +@@ -1964,6 +2061,8 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) + struct gpio_pin_range *pin_range, *tmp; + struct gpio_device *gdev = gc->gpiodev; + ++ deb_debug("\n"); ++ + list_for_each_entry_safe(pin_range, tmp, &gdev->pin_ranges, node) { + list_del(&pin_range->node); + pinctrl_remove_gpio_range(pin_range->pctldev, +@@ -1973,7 +2072,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) + } + EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); + +-#endif /* CONFIG_PINCTRL */ ++ #endif /* CONFIG_PINCTRL */ + + /* These "optional" allocation calls help prevent drivers from stomping + * on each other, and help provide better diagnostics in debugfs. +@@ -1987,6 +2086,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) + bool hogged = false; + unsigned offset; + ++ deb_debug("label=%s\n", label); ++ + if (label) { + /* Free desc->label if already allocated. */ + if (desc->label) { +@@ -2092,6 +2193,8 @@ int gpiod_request(struct gpio_desc *desc, const char *label) + int ret = -EPROBE_DEFER; + struct gpio_device *gdev; + ++ deb_debug("label=%s\n", label); ++ + VALIDATE_DESC(desc); + gdev = desc->gdev; + +@@ -2108,6 +2211,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label) + + return ret; + } ++EXPORT_SYMBOL_GPL(gpiod_request); + + static bool gpiod_free_commit(struct gpio_desc *desc) + { +@@ -2115,6 +2219,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) + unsigned long flags; + struct gpio_chip *gc; + ++ deb_debug("\n"); ++ + might_sleep(); + + gpiod_unexport(desc); +@@ -2141,12 +2247,12 @@ static bool gpiod_free_commit(struct gpio_desc *desc) + clear_bit(FLAG_EDGE_RISING, &desc->flags); + clear_bit(FLAG_EDGE_FALLING, &desc->flags); + clear_bit(FLAG_IS_HOGGED, &desc->flags); +-#ifdef CONFIG_OF_DYNAMIC ++ #ifdef CONFIG_OF_DYNAMIC + desc->hog = NULL; +-#endif +-#ifdef CONFIG_GPIO_CDEV ++ #endif ++ #ifdef CONFIG_GPIO_CDEV + WRITE_ONCE(desc->debounce_period_us, 0); +-#endif ++ #endif + ret = true; + } + +@@ -2159,6 +2265,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) + + void gpiod_free(struct gpio_desc *desc) + { ++ deb_debug("\n"); ++ + if (desc && desc->gdev && gpiod_free_commit(desc)) { + module_put(desc->gdev->owner); + put_device(&desc->gdev->dev); +@@ -2166,6 +2274,7 @@ void gpiod_free(struct gpio_desc *desc) + WARN_ON(extra_checks); + } + } ++EXPORT_SYMBOL_GPL(gpiod_free); + + /** + * gpiochip_is_requested - return string iff signal was requested +@@ -2184,6 +2293,8 @@ const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned offset) + { + struct gpio_desc *desc; + ++ deb_debug("label=%s\n", gc->label); ++ + if (offset >= gc->ngpio) + return NULL; + +@@ -2227,6 +2338,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, + struct gpio_desc *desc = gpiochip_get_desc(gc, hwnum); + int ret; + ++ deb_debug("\n"); ++ + if (IS_ERR(desc)) { + chip_err(gc, "failed to get GPIO descriptor\n"); + return desc; +@@ -2274,6 +2387,8 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); + static int gpio_do_set_config(struct gpio_chip *gc, unsigned int offset, + unsigned long config) + { ++ deb_debug("\n"); ++ + if (!gc->set_config) + return -ENOTSUPP; + +@@ -2286,6 +2401,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) + unsigned long config; + unsigned arg; + ++ deb_debug("\n"); ++ + switch (mode) { + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: +@@ -2305,6 +2422,8 @@ static int gpio_set_bias(struct gpio_desc *desc) + int bias = 0; + int ret = 0; + ++ deb_debug("\n"); ++ + if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) + bias = PIN_CONFIG_BIAS_DISABLE; + else if (test_bit(FLAG_PULL_UP, &desc->flags)) +@@ -2334,6 +2453,8 @@ int gpiod_direction_input(struct gpio_desc *desc) + struct gpio_chip *gc; + int ret = 0; + ++ deb_debug("\n"); ++ + VALIDATE_DESC(desc); + gc = desc->gdev->chip; + +@@ -2381,6 +2502,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) + int val = !!value; + int ret = 0; + ++ deb_debug("\n"); ++ + /* + * It's OK not to specify .direction_output() if the gpiochip is + * output-only, but if there is then not even a .set() operation it +@@ -2431,6 +2554,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) + */ + int gpiod_direction_output_raw(struct gpio_desc *desc, int value) + { ++ deb_debug("\n"); ++ + VALIDATE_DESC(desc); + return gpiod_direction_output_raw_commit(desc, value); + } +@@ -2452,6 +2577,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) + { + int ret; + ++ deb_debug("\n"); ++ + VALIDATE_DESC(desc); + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; +@@ -2523,6 +2650,8 @@ int gpiod_timestamp_control(struct gpio_desc *desc, int enable) + { + struct gpio_chip *chip; + ++ deb_debug("\n"); ++ + VALIDATE_DESC(desc); + chip = desc->gdev->chip; + if (!chip->timestamp_control) { +@@ -2550,6 +2679,8 @@ int gpiod_timestamp_read(struct gpio_desc *desc, u64 *ts) + u64 gpio_ts; + int ret; + ++ deb_debug("\n"); ++ + VALIDATE_DESC(desc); + chip = desc->gdev->chip; + if (!chip->timestamp_read) { +@@ -2578,6 +2709,8 @@ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) + { + struct gpio_chip *gc; + ++ deb_debug("\n"); ++ + VALIDATE_DESC(desc); + gc = desc->gdev->chip; + +@@ -2598,6 +2731,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) + { + unsigned long config; + ++ deb_debug("\n"); ++ + config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); + return gpiod_set_config(desc, config); + } +@@ -2618,6 +2753,8 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) + int gpio; + int rc; + ++ deb_debug("\n"); ++ + VALIDATE_DESC(desc); + /* + * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for +@@ -2652,6 +2789,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_transitory); + */ + int gpiod_is_active_low(const struct gpio_desc *desc) + { ++ deb_debug("\n"); ++ + VALIDATE_DESC(desc); + return test_bit(FLAG_ACTIVE_LOW, &desc->flags); + } +@@ -2663,6 +2802,8 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low); + */ + void gpiod_toggle_active_low(struct gpio_desc *desc) + { ++ deb_debug("\n"); ++ + VALIDATE_DESC_VOID(desc); + change_bit(FLAG_ACTIVE_LOW, &desc->flags); + } +@@ -2696,6 +2837,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) + int offset; + int value; + ++ deb_debug("\n"); ++ + gc = desc->gdev->chip; + offset = gpio_chip_hwgpio(desc); + value = gc->get ? gc->get(gc, offset) : -EIO; +@@ -2707,6 +2850,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) + static int gpio_chip_get_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) + { ++ deb_debug("\n"); ++ + if (gc->get_multiple) { + return gc->get_multiple(gc, mask, bits); + } else if (gc->get) { +@@ -2731,6 +2876,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, + { + int ret, i = 0; + ++ deb_debug("\n"); ++ + /* + * Validate array_info against desc_array and its size. + * It should immediately follow desc_array if both +@@ -2837,6 +2984,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, + */ + int gpiod_get_raw_value(const struct gpio_desc *desc) + { ++ deb_debug("\n"); ++ + VALIDATE_DESC(desc); + /* Should be using gpiod_get_raw_value_cansleep() */ + WARN_ON(desc->gdev->chip->can_sleep); +@@ -2858,6 +3007,8 @@ int gpiod_get_value(const struct gpio_desc *desc) + { + int value; + ++ deb_debug("\n"); ++ + VALIDATE_DESC(desc); + /* Should be using gpiod_get_value_cansleep() */ + WARN_ON(desc->gdev->chip->can_sleep); +@@ -2892,6 +3043,8 @@ int gpiod_get_raw_array_value(unsigned int array_size, + struct gpio_array *array_info, + unsigned long *value_bitmap) + { ++ deb_debug("\n"); ++ + if (!desc_array) + return -EINVAL; + return gpiod_get_array_value_complex(true, false, array_size, +@@ -2918,6 +3071,8 @@ int gpiod_get_array_value(unsigned int array_size, + struct gpio_array *array_info, + unsigned long *value_bitmap) + { ++ deb_debug("\n"); ++ + if (!desc_array) + return -EINVAL; + return gpiod_get_array_value_complex(false, false, array_size, +@@ -2937,6 +3092,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) + struct gpio_chip *gc = desc->gdev->chip; + int offset = gpio_chip_hwgpio(desc); + ++ deb_debug("\n"); ++ + if (value) { + ret = gc->direction_input(gc, offset); + } else { +@@ -2962,6 +3119,8 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value + struct gpio_chip *gc = desc->gdev->chip; + int offset = gpio_chip_hwgpio(desc); + ++ deb_debug("\n"); ++ + if (value) { + ret = gc->direction_output(gc, offset, 1); + if (!ret) +@@ -2980,6 +3139,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) + { + struct gpio_chip *gc; + ++ deb_debug("\n"); ++ + gc = desc->gdev->chip; + trace_gpio_value(desc_to_gpio(desc), 0, value); + gc->set(gc, gpio_chip_hwgpio(desc), value); +@@ -2998,6 +3159,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) + static void gpio_chip_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) + { ++ deb_debug("\n"); ++ + if (gc->set_multiple) { + gc->set_multiple(gc, mask, bits); + } else { +@@ -3017,6 +3180,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, + { + int i = 0; + ++ deb_debug("\n"); ++ + /* + * Validate array_info against desc_array and its size. + * It should immediately follow desc_array if both +@@ -3122,6 +3287,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, + */ + void gpiod_set_raw_value(struct gpio_desc *desc, int value) + { ++ deb_debug("\n"); ++ + VALIDATE_DESC_VOID(desc); + /* Should be using gpiod_set_raw_value_cansleep() */ + WARN_ON(desc->gdev->chip->can_sleep); +@@ -3140,6 +3307,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); + */ + static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) + { ++ deb_debug("\n"); ++ + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) +@@ -3163,6 +3332,8 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) + */ + void gpiod_set_value(struct gpio_desc *desc, int value) + { ++ deb_debug("\n"); ++ + VALIDATE_DESC_VOID(desc); + /* Should be using gpiod_set_value_cansleep() */ + WARN_ON(desc->gdev->chip->can_sleep); +@@ -3188,6 +3359,8 @@ int gpiod_set_raw_array_value(unsigned int array_size, + struct gpio_array *array_info, + unsigned long *value_bitmap) + { ++ deb_debug("\n"); ++ + if (!desc_array) + return -EINVAL; + return gpiod_set_array_value_complex(true, false, array_size, +@@ -3213,6 +3386,8 @@ int gpiod_set_array_value(unsigned int array_size, + struct gpio_array *array_info, + unsigned long *value_bitmap) + { ++ deb_debug("\n"); ++ + if (!desc_array) + return -EINVAL; + return gpiod_set_array_value_complex(false, false, array_size, +@@ -3228,6 +3403,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); + */ + int gpiod_cansleep(const struct gpio_desc *desc) + { ++ deb_debug("\n"); ++ + VALIDATE_DESC(desc); + return desc->gdev->chip->can_sleep; + } +@@ -3240,6 +3417,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); + */ + int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name) + { ++ deb_debug("\n"); ++ + VALIDATE_DESC(desc); + if (name) { + name = kstrdup_const(name, GFP_KERNEL); +@@ -3266,6 +3445,8 @@ int gpiod_to_irq(const struct gpio_desc *desc) + struct gpio_chip *gc; + int offset; + ++ deb_debug("\n"); ++ + /* + * Cannot VALIDATE_DESC() here as gpiod_to_irq() consumer semantics + * requires this function to not return zero on an invalid descriptor +@@ -3300,6 +3481,8 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq); + int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset) + { + struct gpio_desc *desc; ++ ++ deb_debug("\n"); + + desc = gpiochip_get_desc(gc, offset); + if (IS_ERR(desc)) +@@ -3355,6 +3538,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) + { + struct gpio_desc *desc; + ++ deb_debug("\n"); ++ + desc = gpiochip_get_desc(gc, offset); + if (IS_ERR(desc)) + return; +@@ -3372,6 +3557,8 @@ void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset) + { + struct gpio_desc *desc = gpiochip_get_desc(gc, offset); + ++ deb_debug("\n"); ++ + if (!IS_ERR(desc) && + !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) + clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); +@@ -3382,6 +3569,8 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset) + { + struct gpio_desc *desc = gpiochip_get_desc(gc, offset); + ++ deb_debug("\n"); ++ + if (!IS_ERR(desc) && + !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) { + /* +@@ -3397,6 +3586,8 @@ EXPORT_SYMBOL_GPL(gpiochip_enable_irq); + + bool gpiochip_line_is_irq(struct gpio_chip *gc, unsigned int offset) + { ++ deb_debug("\n"); ++ + if (offset >= gc->ngpio) + return false; + +@@ -3430,6 +3621,8 @@ EXPORT_SYMBOL_GPL(gpiochip_relres_irq); + + bool gpiochip_line_is_open_drain(struct gpio_chip *gc, unsigned int offset) + { ++ deb_debug("\n"); ++ + if (offset >= gc->ngpio) + return false; + +@@ -3439,6 +3632,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); + + bool gpiochip_line_is_open_source(struct gpio_chip *gc, unsigned int offset) + { ++ deb_debug("\n"); ++ + if (offset >= gc->ngpio) + return false; + +@@ -3448,6 +3643,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); + + bool gpiochip_line_is_persistent(struct gpio_chip *gc, unsigned int offset) + { ++ deb_debug("\n"); ++ + if (offset >= gc->ngpio) + return false; + +@@ -3466,6 +3663,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); + */ + int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) + { ++ deb_debug("\n"); ++ + might_sleep_if(extra_checks); + VALIDATE_DESC(desc); + return gpiod_get_raw_value_commit(desc); +@@ -3485,6 +3684,8 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) + { + int value; + ++ deb_debug("\n"); ++ + might_sleep_if(extra_checks); + VALIDATE_DESC(desc); + value = gpiod_get_raw_value_commit(desc); +@@ -3516,6 +3717,8 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, + struct gpio_array *array_info, + unsigned long *value_bitmap) + { ++ deb_debug("\n"); ++ + might_sleep_if(extra_checks); + if (!desc_array) + return -EINVAL; +@@ -3542,6 +3745,8 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, + struct gpio_array *array_info, + unsigned long *value_bitmap) + { ++ deb_debug("\n"); ++ + might_sleep_if(extra_checks); + if (!desc_array) + return -EINVAL; +@@ -3563,6 +3768,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); + */ + void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) + { ++ deb_debug("\n"); ++ + might_sleep_if(extra_checks); + VALIDATE_DESC_VOID(desc); + gpiod_set_raw_value_commit(desc, value); +@@ -3581,6 +3788,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); + */ + void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) + { ++ deb_debug("\n"); ++ + might_sleep_if(extra_checks); + VALIDATE_DESC_VOID(desc); + gpiod_set_value_nocheck(desc, value); +@@ -3604,6 +3813,8 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, + struct gpio_array *array_info, + unsigned long *value_bitmap) + { ++ deb_debug("\n"); ++ + might_sleep_if(extra_checks); + if (!desc_array) + return -EINVAL; +@@ -3621,6 +3832,8 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) + { + unsigned int i; + ++ deb_debug("\n"); ++ + mutex_lock(&gpio_lookup_lock); + + for (i = 0; i < n; i++) +@@ -3646,6 +3859,8 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, + struct gpio_array *array_info, + unsigned long *value_bitmap) + { ++ deb_debug("\n"); ++ + might_sleep_if(extra_checks); + if (!desc_array) + return -EINVAL; +@@ -3661,6 +3876,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); + */ + void gpiod_add_lookup_table(struct gpiod_lookup_table *table) + { ++ deb_debug("\n"); ++ + mutex_lock(&gpio_lookup_lock); + + list_add_tail(&table->list, &gpio_lookup_list); +@@ -3675,6 +3892,8 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); + */ + void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) + { ++ deb_debug("\n"); ++ + mutex_lock(&gpio_lookup_lock); + + list_del(&table->list); +@@ -3692,6 +3911,8 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) + struct gpio_chip *gc; + struct gpiod_hog *hog; + ++ deb_debug("\n"); ++ + mutex_lock(&gpio_machine_hogs_mutex); + + for (hog = &hogs[0]; hog->chip_label; hog++) { +@@ -3715,6 +3936,8 @@ static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) + const char *dev_id = dev ? dev_name(dev) : NULL; + struct gpiod_lookup_table *table; + ++ deb_debug("\n"); ++ + mutex_lock(&gpio_lookup_lock); + + list_for_each_entry(table, &gpio_lookup_list, list) { +@@ -3748,6 +3971,8 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, + struct gpiod_lookup_table *table; + struct gpiod_lookup *p; + ++ deb_debug("\n"); ++ + table = gpiod_find_lookup_table(dev); + if (!table) + return desc; +@@ -3813,6 +4038,8 @@ static int platform_gpio_count(struct device *dev, const char *con_id) + struct gpiod_lookup *p; + unsigned int count = 0; + ++ deb_debug("\n"); ++ + table = gpiod_find_lookup_table(dev); + if (!table) + return -ENOENT; +@@ -3858,6 +4085,8 @@ struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, + char prop_name[32]; /* 32 is max size of property name */ + unsigned int i; + ++ deb_debug("\n"); ++ + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { + if (con_id) + snprintf(prop_name, sizeof(prop_name), "%s-%s", +@@ -3886,6 +4115,8 @@ int gpiod_count(struct device *dev, const char *con_id) + { + int count = -ENOENT; + ++ deb_debug("\n"); ++ + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) + count = of_gpio_get_count(dev, con_id); + else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) +@@ -3911,6 +4142,8 @@ EXPORT_SYMBOL_GPL(gpiod_count); + struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id, + enum gpiod_flags flags) + { ++ deb_debug("\n"); ++ + return gpiod_get_index(dev, con_id, 0, flags); + } + EXPORT_SYMBOL_GPL(gpiod_get); +@@ -3951,6 +4184,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, + { + int ret; + ++ deb_debug("\n"); ++ + if (lflags & GPIO_ACTIVE_LOW) + set_bit(FLAG_ACTIVE_LOW, &desc->flags); + +@@ -4121,6 +4356,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, + struct gpio_desc *desc = ERR_PTR(-ENODEV); + int ret; + ++ deb_debug("\n"); ++ + if (!fwnode) + return ERR_PTR(-EINVAL); + +@@ -4178,6 +4415,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, + { + struct gpio_desc *desc; + ++ deb_debug("\n"); ++ + desc = gpiod_get_index(dev, con_id, index, flags); + if (IS_ERR(desc)) { + if (PTR_ERR(desc) == -ENOENT) +@@ -4204,6 +4443,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, + int hwnum; + int ret; + ++ deb_debug("\n"); ++ + gc = gpiod_to_chip(desc); + hwnum = gpio_chip_hwgpio(desc); + +@@ -4235,6 +4476,8 @@ static void gpiochip_free_hogs(struct gpio_chip *gc) + { + int id; + ++ deb_debug("\n"); ++ + for (id = 0; id < gc->ngpio; id++) { + if (test_bit(FLAG_IS_HOGGED, &gc->gpiodev->descs[id].flags)) + gpiochip_free_own_desc(&gc->gpiodev->descs[id]); +@@ -4263,6 +4506,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, + struct gpio_chip *gc; + int count, bitmap_size; + ++ deb_debug("\n"); ++ + count = gpiod_count(dev, con_id); + if (count < 0) + return ERR_PTR(count); +@@ -4383,6 +4628,8 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, + { + struct gpio_descs *descs; + ++ deb_debug("\n"); ++ + descs = gpiod_get_array(dev, con_id, flags); + if (PTR_ERR(descs) == -ENOENT) + return NULL; +@@ -4399,6 +4646,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); + */ + void gpiod_put(struct gpio_desc *desc) + { ++ deb_debug("\n"); ++ + if (desc) + gpiod_free(desc); + } +@@ -4412,6 +4661,8 @@ void gpiod_put_array(struct gpio_descs *descs) + { + unsigned int i; + ++ deb_debug("\n"); ++ + for (i = 0; i < descs->ndescs; i++) + gpiod_put(descs->desc[i]); + +@@ -4423,6 +4674,8 @@ static int __init gpiolib_dev_init(void) + { + int ret; + ++ deb_debug("\n"); ++ + /* Register GPIO sysfs bus */ + ret = bus_register(&gpio_bus_type); + if (ret < 0) { +@@ -4442,13 +4695,13 @@ static int __init gpiolib_dev_init(void) + + #if IS_ENABLED(CONFIG_OF_DYNAMIC) && IS_ENABLED(CONFIG_OF_GPIO) + WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier)); +-#endif /* CONFIG_OF_DYNAMIC && CONFIG_OF_GPIO */ ++ #endif /* CONFIG_OF_DYNAMIC && CONFIG_OF_GPIO */ + + return ret; + } + core_initcall(gpiolib_dev_init); + +-#ifdef CONFIG_DEBUG_FS ++ #ifdef CONFIG_DEBUG_FS + + static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) + { +@@ -4575,4 +4828,4 @@ static int __init gpiolib_debugfs_init(void) + } + subsys_initcall(gpiolib_debugfs_init); + +-#endif /* DEBUG_FS */ ++ #endif /* DEBUG_FS */ +diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c +index c73b34e03aae..b31c7f3cb6be 100644 +--- a/drivers/pinctrl/core.c ++++ b/drivers/pinctrl/core.c +@@ -26,16 +26,25 @@ + #include + #include + +-#ifdef CONFIG_GPIOLIB ++ #ifdef CONFIG_GPIOLIB + #include "../gpio/gpiolib.h" + #include +-#endif ++ #endif + + #include "core.h" + #include "devicetree.h" + #include "pinmux.h" + #include "pinconf.h" + ++// #define GPIO_DEBUG ++ ++#ifdef GPIO_DEBUG ++#define deb_info(fmt, ...) printk(KERN_INFO"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_debug(fmt, ...) printk(KERN_DEBUG"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#else ++#define deb_info(fmt, ...) ++#define deb_debug(fmt, ...) ++#endif + + static bool pinctrl_dummy_state; + +@@ -101,6 +110,9 @@ struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *devname) + { + struct pinctrl_dev *pctldev; + ++ // removed because prints too often ++ // deb_debug("\n"); ++ + if (!devname) + return NULL; + +@@ -123,6 +135,9 @@ struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np) + { + struct pinctrl_dev *pctldev; + ++ // removed because it prints too often ++ // deb_debug("\n"); ++ + mutex_lock(&pinctrldev_list_mutex); + + list_for_each_entry(pctldev, &pinctrldev_list, node) +@@ -145,6 +160,8 @@ int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name) + { + unsigned i, pin; + ++ deb_debug("\n"); ++ + /* The pin number can be retrived from the pin controller descriptor */ + for (i = 0; i < pctldev->desc->npins; i++) { + struct pin_desc *desc; +@@ -168,6 +185,8 @@ const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin) + { + const struct pin_desc *desc; + ++ deb_debug("\n"); ++ + desc = pin_desc_get(pctldev, pin); + if (!desc) { + dev_err(pctldev->dev, "failed to get pin(%d) name\n", +@@ -186,6 +205,9 @@ static void pinctrl_free_pindescs(struct pinctrl_dev *pctldev, + { + int i; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + for (i = 0; i < num_pins; i++) { + struct pin_desc *pindesc; + +@@ -206,6 +228,9 @@ static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev, + { + struct pin_desc *pindesc; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + pindesc = pin_desc_get(pctldev, pin->number); + if (pindesc) { + dev_err(pctldev->dev, "pin %d already registered\n", +@@ -247,6 +272,9 @@ static int pinctrl_register_pins(struct pinctrl_dev *pctldev, + unsigned i; + int ret = 0; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + for (i = 0; i < num_descs; i++) { + ret = pinctrl_register_one_pin(pctldev, &pins[i]); + if (ret) +@@ -273,6 +301,9 @@ static inline int gpio_to_pin(struct pinctrl_gpio_range *range, + unsigned int gpio) + { + unsigned int offset = gpio - range->base; ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (range->pins) + return range->pins[offset]; + else +@@ -292,6 +323,9 @@ pinctrl_match_gpio_range(struct pinctrl_dev *pctldev, unsigned gpio) + { + struct pinctrl_gpio_range *range; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + mutex_lock(&pctldev->mutex); + /* Loop over the ranges */ + list_for_each_entry(range, &pctldev->gpio_ranges, node) { +@@ -319,13 +353,16 @@ pinctrl_match_gpio_range(struct pinctrl_dev *pctldev, unsigned gpio) + * certain GPIO pin doesn't have back-end pinctrl device. If the return value + * is false, it means that pinctrl device may not be ready. + */ +-#ifdef CONFIG_GPIOLIB ++ #ifdef CONFIG_GPIOLIB + static bool pinctrl_ready_for_gpio_range(unsigned gpio) + { + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range *range = NULL; + struct gpio_chip *chip = gpio_to_chip(gpio); + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (WARN(!chip, "no gpio_chip for gpio%i?", gpio)) + return false; + +@@ -353,7 +390,7 @@ static bool pinctrl_ready_for_gpio_range(unsigned gpio) + } + #else + static bool pinctrl_ready_for_gpio_range(unsigned gpio) { return true; } +-#endif ++ #endif + + /** + * pinctrl_get_device_gpio_range() - find device for GPIO range +@@ -372,6 +409,9 @@ static int pinctrl_get_device_gpio_range(unsigned gpio, + { + struct pinctrl_dev *pctldev; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + mutex_lock(&pinctrldev_list_mutex); + + /* Loop over the pin controllers */ +@@ -403,6 +443,9 @@ static int pinctrl_get_device_gpio_range(unsigned gpio, + void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range) + { ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + mutex_lock(&pctldev->mutex); + list_add_tail(&range->node, &pctldev->gpio_ranges); + mutex_unlock(&pctldev->mutex); +@@ -415,6 +458,9 @@ void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, + { + int i; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + for (i = 0; i < nranges; i++) + pinctrl_add_gpio_range(pctldev, &ranges[i]); + } +@@ -425,6 +471,9 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname, + { + struct pinctrl_dev *pctldev; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + pctldev = get_pinctrl_dev_from_devname(devname); + + /* +@@ -447,6 +496,9 @@ int pinctrl_get_group_pins(struct pinctrl_dev *pctldev, const char *pin_group, + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + int gs; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (!pctlops->get_group_pins) + return -EINVAL; + +@@ -464,6 +516,9 @@ pinctrl_find_gpio_range_from_pin_nolock(struct pinctrl_dev *pctldev, + { + struct pinctrl_gpio_range *range; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + /* Loop over the ranges */ + list_for_each_entry(range, &pctldev->gpio_ranges, node) { + /* Check if we're in the valid range */ +@@ -493,6 +548,9 @@ pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev, + { + struct pinctrl_gpio_range *range; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + mutex_lock(&pctldev->mutex); + range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin); + mutex_unlock(&pctldev->mutex); +@@ -509,13 +567,16 @@ EXPORT_SYMBOL_GPL(pinctrl_find_gpio_range_from_pin); + void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range) + { ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + mutex_lock(&pctldev->mutex); + list_del(&range->node); + mutex_unlock(&pctldev->mutex); + } + EXPORT_SYMBOL_GPL(pinctrl_remove_gpio_range); + +-#ifdef CONFIG_GENERIC_PINCTRL_GROUPS ++ #ifdef CONFIG_GENERIC_PINCTRL_GROUPS + + /** + * pinctrl_generic_get_group_count() - returns the number of pin groups +@@ -537,6 +598,9 @@ const char *pinctrl_generic_get_group_name(struct pinctrl_dev *pctldev, + { + struct group_desc *group; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + group = radix_tree_lookup(&pctldev->pin_group_tree, + selector); + if (!group) +@@ -560,6 +624,9 @@ int pinctrl_generic_get_group_pins(struct pinctrl_dev *pctldev, + { + struct group_desc *group; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + group = radix_tree_lookup(&pctldev->pin_group_tree, + selector); + if (!group) { +@@ -585,6 +652,9 @@ struct group_desc *pinctrl_generic_get_group(struct pinctrl_dev *pctldev, + { + struct group_desc *group; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + group = radix_tree_lookup(&pctldev->pin_group_tree, + selector); + if (!group) +@@ -630,6 +700,9 @@ int pinctrl_generic_add_group(struct pinctrl_dev *pctldev, const char *name, + struct group_desc *group; + int selector; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (!name) + return -EINVAL; + +@@ -668,6 +741,9 @@ int pinctrl_generic_remove_group(struct pinctrl_dev *pctldev, + { + struct group_desc *group; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + group = radix_tree_lookup(&pctldev->pin_group_tree, + selector); + if (!group) +@@ -704,8 +780,8 @@ static void pinctrl_generic_free_groups(struct pinctrl_dev *pctldev) + static inline void pinctrl_generic_free_groups(struct pinctrl_dev *pctldev) + { + } +-#endif /* CONFIG_GENERIC_PINCTRL_GROUPS */ +- ++ #endif /* CONFIG_GENERIC_PINCTRL_GROUPS */ ++ + /** + * pinctrl_get_group_selector() - returns the group selector for a group + * @pctldev: the pin controller handling the group +@@ -718,6 +794,9 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, + unsigned ngroups = pctlops->get_groups_count(pctldev); + unsigned group_selector = 0; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + while (group_selector < ngroups) { + const char *gname = pctlops->get_group_name(pctldev, + group_selector); +@@ -745,6 +824,9 @@ bool pinctrl_gpio_can_use_line(unsigned gpio) + bool result; + int pin; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + /* + * Try to obtain GPIO range, if it fails + * we're probably dealing with GPIO driver +@@ -781,6 +863,9 @@ int pinctrl_gpio_request(unsigned gpio) + int ret; + int pin; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); + if (ret) { + if (pinctrl_ready_for_gpio_range(gpio)) +@@ -816,6 +901,8 @@ void pinctrl_gpio_free(unsigned gpio) + int ret; + int pin; + ++ deb_debug("\n"); ++ + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); + if (ret) { + return; +@@ -838,6 +925,9 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input) + int ret; + int pin; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); + if (ret) { + return ret; +@@ -864,6 +954,9 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input) + */ + int pinctrl_gpio_direction_input(unsigned gpio) + { ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + return pinctrl_gpio_direction(gpio, true); + } + EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); +@@ -878,6 +971,9 @@ EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); + */ + int pinctrl_gpio_direction_output(unsigned gpio) + { ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + return pinctrl_gpio_direction(gpio, false); + } + EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_output); +@@ -898,6 +994,8 @@ int pinctrl_gpio_set_config(unsigned gpio, unsigned long config) + struct pinctrl_dev *pctldev; + int ret, pin; + ++ deb_debug("\n"); ++ + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); + if (ret) + return ret; +@@ -916,6 +1014,9 @@ static struct pinctrl_state *find_state(struct pinctrl *p, + { + struct pinctrl_state *state; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + list_for_each_entry(state, &p->states, node) + if (!strcmp(state->name, name)) + return state; +@@ -928,6 +1029,9 @@ static struct pinctrl_state *create_state(struct pinctrl *p, + { + struct pinctrl_state *state; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return ERR_PTR(-ENOMEM); +@@ -947,6 +1051,9 @@ static int add_setting(struct pinctrl *p, struct pinctrl_dev *pctldev, + struct pinctrl_setting *setting; + int ret; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + state = find_state(p, map->name); + if (!state) + state = create_state(p, map->name); +@@ -1009,6 +1116,9 @@ static struct pinctrl *find_pinctrl(struct device *dev) + { + struct pinctrl *p; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + mutex_lock(&pinctrl_list_mutex); + list_for_each_entry(p, &pinctrl_list, node) + if (p->dev == dev) { +@@ -1032,6 +1142,9 @@ static struct pinctrl *create_pinctrl(struct device *dev, + const struct pinctrl_map *map; + int ret; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + /* + * create the state cookie holder struct pinctrl for each + * mapping, this is what consumers will get when requesting +@@ -1115,6 +1228,9 @@ struct pinctrl *pinctrl_get(struct device *dev) + { + struct pinctrl *p; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (WARN_ON(!dev)) + return ERR_PTR(-EINVAL); + +@@ -1137,6 +1253,9 @@ EXPORT_SYMBOL_GPL(pinctrl_get); + static void pinctrl_free_setting(bool disable_setting, + struct pinctrl_setting *setting) + { ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + switch (setting->type) { + case PIN_MAP_TYPE_MUX_GROUP: + if (disable_setting) +@@ -1157,6 +1276,9 @@ static void pinctrl_free(struct pinctrl *p, bool inlist) + struct pinctrl_state *state, *n1; + struct pinctrl_setting *setting, *n2; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + mutex_lock(&pinctrl_list_mutex); + list_for_each_entry_safe(state, n1, &p->states, node) { + list_for_each_entry_safe(setting, n2, &state->settings, node) { +@@ -1184,6 +1306,9 @@ static void pinctrl_release(struct kref *kref) + { + struct pinctrl *p = container_of(kref, struct pinctrl, users); + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + pinctrl_free(p, true); + } + +@@ -1193,6 +1318,9 @@ static void pinctrl_release(struct kref *kref) + */ + void pinctrl_put(struct pinctrl *p) + { ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + kref_put(&p->users, pinctrl_release); + } + EXPORT_SYMBOL_GPL(pinctrl_put); +@@ -1207,6 +1335,9 @@ struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, + { + struct pinctrl_state *state; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + state = find_state(p, name); + if (!state) { + if (pinctrl_dummy_state) { +@@ -1225,6 +1356,9 @@ EXPORT_SYMBOL_GPL(pinctrl_lookup_state); + static void pinctrl_link_add(struct pinctrl_dev *pctldev, + struct device *consumer) + { ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (pctldev->desc->link_consumers) + device_link_add(consumer, pctldev->dev, + DL_FLAG_PM_RUNTIME | +@@ -1242,6 +1376,9 @@ static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state) + struct pinctrl_state *old_state = p->state; + int ret; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (p->state) { + /* + * For each pinmux setting in the old state, forget SW's record +@@ -1289,6 +1426,8 @@ static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state) + unapply_new_state: + dev_err(p->dev, "Error applying setting, reverse things back\n"); + ++ deb_debug("\n"); ++ + list_for_each_entry(setting2, &state->settings, node) { + if (&setting2->node == &setting->node) + break; +@@ -1317,6 +1456,9 @@ static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state) + */ + int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) + { ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (p->state == state) + return 0; + +@@ -1340,6 +1482,9 @@ struct pinctrl *devm_pinctrl_get(struct device *dev) + { + struct pinctrl **ptr, *p; + ++ // printk removed because it triggers far to often ++ // deb_debug("\n"); ++ + ptr = devres_alloc(devm_pinctrl_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); +@@ -1360,6 +1505,9 @@ static int devm_pinctrl_match(struct device *dev, void *res, void *data) + { + struct pinctrl **p = res; + ++ // printk removed because it triggers far to often ++ // deb_debug("\n"); ++ + return *p == data; + } + +@@ -1373,6 +1521,9 @@ static int devm_pinctrl_match(struct device *dev, void *res, void *data) + */ + void devm_pinctrl_put(struct pinctrl *p) + { ++ // printk removed because it triggers far to often ++ // deb_debug("\n"); ++ + WARN_ON(devres_release(p->dev, devm_pinctrl_release, + devm_pinctrl_match, p)); + } +@@ -1391,6 +1542,8 @@ int pinctrl_register_mappings(const struct pinctrl_map *maps, + int i, ret; + struct pinctrl_maps *maps_node; + ++ deb_debug("\n"); ++ + pr_debug("add %u pinctrl maps\n", num_maps); + + /* First sanity check the new mapping */ +@@ -1459,6 +1612,8 @@ void pinctrl_unregister_mappings(const struct pinctrl_map *map) + { + struct pinctrl_maps *maps_node; + ++ deb_debug("\n"); ++ + mutex_lock(&pinctrl_maps_mutex); + list_for_each_entry(maps_node, &pinctrl_maps, node) { + if (maps_node->maps == map) { +@@ -1478,6 +1633,8 @@ EXPORT_SYMBOL_GPL(pinctrl_unregister_mappings); + */ + int pinctrl_force_sleep(struct pinctrl_dev *pctldev) + { ++ deb_debug("\n"); ++ + if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_sleep)) + return pinctrl_commit_state(pctldev->p, pctldev->hog_sleep); + return 0; +@@ -1490,6 +1647,8 @@ EXPORT_SYMBOL_GPL(pinctrl_force_sleep); + */ + int pinctrl_force_default(struct pinctrl_dev *pctldev) + { ++ deb_debug("\n"); ++ + if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_default)) + return pinctrl_commit_state(pctldev->p, pctldev->hog_default); + return 0; +@@ -1509,6 +1668,9 @@ int pinctrl_init_done(struct device *dev) + struct dev_pin_info *pins = dev->pins; + int ret; + ++ // printk removed because it triggers far to often ++ // deb_debug("\n"); ++ + if (!pins) + return 0; + +@@ -1534,6 +1696,9 @@ static int pinctrl_select_bound_state(struct device *dev, + struct dev_pin_info *pins = dev->pins; + int ret; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (IS_ERR(state)) + return 0; /* No such state */ + ret = pinctrl_select_state(pins->p, state); +@@ -1549,6 +1714,9 @@ static int pinctrl_select_bound_state(struct device *dev, + */ + int pinctrl_select_default_state(struct device *dev) + { ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (!dev->pins) + return 0; + +@@ -1556,7 +1724,7 @@ int pinctrl_select_default_state(struct device *dev) + } + EXPORT_SYMBOL_GPL(pinctrl_select_default_state); + +-#ifdef CONFIG_PM ++ #ifdef CONFIG_PM + + /** + * pinctrl_pm_select_default_state() - select default pinctrl state for PM +@@ -1564,6 +1732,9 @@ EXPORT_SYMBOL_GPL(pinctrl_select_default_state); + */ + int pinctrl_pm_select_default_state(struct device *dev) + { ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + return pinctrl_select_default_state(dev); + } + EXPORT_SYMBOL_GPL(pinctrl_pm_select_default_state); +@@ -1587,26 +1758,32 @@ EXPORT_SYMBOL_GPL(pinctrl_pm_select_sleep_state); + */ + int pinctrl_pm_select_idle_state(struct device *dev) + { ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (!dev->pins) + return 0; + + return pinctrl_select_bound_state(dev, dev->pins->idle_state); + } + EXPORT_SYMBOL_GPL(pinctrl_pm_select_idle_state); +-#endif ++ #endif + +-#ifdef CONFIG_DEBUG_FS ++ #ifdef CONFIG_DEBUG_FS + + static int pinctrl_pins_show(struct seq_file *s, void *what) + { + struct pinctrl_dev *pctldev = s->private; + const struct pinctrl_ops *ops = pctldev->desc->pctlops; + unsigned i, pin; +-#ifdef CONFIG_GPIOLIB ++ #ifdef CONFIG_GPIOLIB + struct pinctrl_gpio_range *range; + struct gpio_chip *chip; + int gpio_num; +-#endif ++ #endif ++ ++ // removed because it triggers far too often ++ // deb_debug("\n"); + + seq_printf(s, "registered pins: %d\n", pctldev->desc->npins); + +@@ -1624,7 +1801,7 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) + + seq_printf(s, "pin %d (%s) ", pin, desc->name); + +-#ifdef CONFIG_GPIOLIB ++ #ifdef CONFIG_GPIOLIB + gpio_num = -1; + list_for_each_entry(range, &pctldev->gpio_ranges, node) { + if ((pin >= range->pin_base) && +@@ -1641,7 +1818,7 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) + seq_printf(s, "%u:%s ", gpio_num - chip->gpiodev->base, chip->label); + else + seq_puts(s, "0:? "); +-#endif ++ #endif + + /* Driver-specific info per pin */ + if (ops->pin_dbg_show) +@@ -1662,6 +1839,9 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) + const struct pinctrl_ops *ops = pctldev->desc->pctlops; + unsigned ngroups, selector = 0; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + mutex_lock(&pctldev->mutex); + + ngroups = ops->get_groups_count(pctldev); +@@ -1707,6 +1887,8 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what) + struct pinctrl_dev *pctldev = s->private; + struct pinctrl_gpio_range *range; + ++ deb_debug("\n"); ++ + seq_puts(s, "GPIO ranges handled:\n"); + + mutex_lock(&pctldev->mutex); +@@ -1740,6 +1922,8 @@ static int pinctrl_devices_show(struct seq_file *s, void *what) + { + struct pinctrl_dev *pctldev; + ++ deb_debug("\n"); ++ + seq_puts(s, "name [pinmux] [pinconf]\n"); + + mutex_lock(&pinctrldev_list_mutex); +@@ -1773,6 +1957,8 @@ static inline const char *map_type(enum pinctrl_map_type type) + "CONFIGS_GROUP", + }; + ++ deb_debug("\n"); ++ + if (type >= ARRAY_SIZE(names)) + return "UNKNOWN"; + +@@ -1785,6 +1971,8 @@ static int pinctrl_maps_show(struct seq_file *s, void *what) + int i; + const struct pinctrl_map *map; + ++ deb_debug("\n"); ++ + seq_puts(s, "Pinctrl maps:\n"); + + mutex_lock(&pinctrl_maps_mutex); +@@ -1823,6 +2011,8 @@ static int pinctrl_show(struct seq_file *s, void *what) + struct pinctrl_state *state; + struct pinctrl_setting *setting; + ++ deb_debug("\n"); ++ + seq_puts(s, "Requested pin control handlers their pinmux maps:\n"); + + mutex_lock(&pinctrl_list_mutex); +@@ -1870,6 +2060,9 @@ static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) + struct dentry *device_root; + const char *debugfs_name; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (pctldev->desc->name && + strcmp(dev_name(pctldev->dev), pctldev->desc->name)) { + debugfs_name = devm_kasprintf(pctldev->dev, GFP_KERNEL, +@@ -1906,11 +2099,15 @@ static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) + + static void pinctrl_remove_device_debugfs(struct pinctrl_dev *pctldev) + { ++ deb_debug("\n"); ++ + debugfs_remove_recursive(pctldev->device_root); + } + + static void pinctrl_init_debugfs(void) + { ++ deb_debug("\n"); ++ + debugfs_root = debugfs_create_dir("pinctrl", NULL); + if (IS_ERR(debugfs_root) || !debugfs_root) { + pr_warn("failed to create debugfs directory\n"); +@@ -1930,22 +2127,30 @@ static void pinctrl_init_debugfs(void) + + static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) + { ++ deb_debug("\n"); ++ + } + + static void pinctrl_init_debugfs(void) + { ++ deb_debug("\n"); ++ + } + + static void pinctrl_remove_device_debugfs(struct pinctrl_dev *pctldev) + { +-} ++ deb_debug("\n"); + +-#endif ++} ++ #endif + + static int pinctrl_check_ops(struct pinctrl_dev *pctldev) + { + const struct pinctrl_ops *ops = pctldev->desc->pctlops; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + if (!ops || + !ops->get_groups_count || + !ops->get_group_name) +@@ -1967,6 +2172,8 @@ pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev, + struct pinctrl_dev *pctldev; + int ret; + ++ deb_debug("\n"); ++ + if (!pctldesc) + return ERR_PTR(-EINVAL); + if (!pctldesc->name) +@@ -1981,12 +2188,12 @@ pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev, + pctldev->desc = pctldesc; + pctldev->driver_data = driver_data; + INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL); +-#ifdef CONFIG_GENERIC_PINCTRL_GROUPS ++ #ifdef CONFIG_GENERIC_PINCTRL_GROUPS + INIT_RADIX_TREE(&pctldev->pin_group_tree, GFP_KERNEL); +-#endif +-#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS ++ #endif ++ #ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS + INIT_RADIX_TREE(&pctldev->pin_function_tree, GFP_KERNEL); +-#endif ++ #endif + INIT_LIST_HEAD(&pctldev->gpio_ranges); + INIT_LIST_HEAD(&pctldev->node); + pctldev->dev = dev; +@@ -2033,6 +2240,9 @@ pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev, + + static int pinctrl_claim_hogs(struct pinctrl_dev *pctldev) + { ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + pctldev->p = create_pinctrl(pctldev->dev, pctldev); + if (PTR_ERR(pctldev->p) == -ENODEV) { + dev_dbg(pctldev->dev, "no hogs found\n"); +@@ -2073,6 +2283,8 @@ int pinctrl_enable(struct pinctrl_dev *pctldev) + { + int error; + ++ deb_debug("\n"); ++ + error = pinctrl_claim_hogs(pctldev); + if (error) { + dev_err(pctldev->dev, "could not claim hogs: %i\n", +@@ -2112,6 +2324,8 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, + struct pinctrl_dev *pctldev; + int error; + ++ deb_debug("\n"); ++ + pctldev = pinctrl_init_controller(pctldesc, dev, driver_data); + if (IS_ERR(pctldev)) + return pctldev; +@@ -2140,6 +2354,8 @@ int pinctrl_register_and_init(struct pinctrl_desc *pctldesc, + { + struct pinctrl_dev *p; + ++ deb_debug("\n"); ++ + p = pinctrl_init_controller(pctldesc, dev, driver_data); + if (IS_ERR(p)) + return PTR_ERR(p); +@@ -2166,6 +2382,8 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) + { + struct pinctrl_gpio_range *range, *n; + ++ deb_debug("\n"); ++ + if (!pctldev) + return; + +@@ -2200,6 +2418,8 @@ static void devm_pinctrl_dev_release(struct device *dev, void *res) + { + struct pinctrl_dev *pctldev = *(struct pinctrl_dev **)res; + ++ deb_debug("\n"); ++ + pinctrl_unregister(pctldev); + } + +@@ -2207,6 +2427,8 @@ static int devm_pinctrl_dev_match(struct device *dev, void *res, void *data) + { + struct pctldev **r = res; + ++ deb_debug("\n"); ++ + if (WARN_ON(!r || !*r)) + return 0; + +@@ -2230,6 +2452,9 @@ struct pinctrl_dev *devm_pinctrl_register(struct device *dev, + { + struct pinctrl_dev **ptr, *pctldev; + ++ // removed because it triggers far too often ++ // deb_debug("\n"); ++ + ptr = devres_alloc(devm_pinctrl_dev_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); +@@ -2266,6 +2491,8 @@ int devm_pinctrl_register_and_init(struct device *dev, + struct pinctrl_dev **ptr; + int error; + ++ deb_debug("\n"); ++ + ptr = devres_alloc(devm_pinctrl_dev_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; +@@ -2290,6 +2517,8 @@ EXPORT_SYMBOL_GPL(devm_pinctrl_register_and_init); + */ + void devm_pinctrl_unregister(struct device *dev, struct pinctrl_dev *pctldev) + { ++ deb_debug("\n"); ++ + WARN_ON(devres_release(dev, devm_pinctrl_dev_release, + devm_pinctrl_dev_match, pctldev)); + } +@@ -2297,6 +2526,8 @@ EXPORT_SYMBOL_GPL(devm_pinctrl_unregister); + + static int __init pinctrl_init(void) + { ++ deb_debug("\n"); ++ + pr_info("initialized pinctrl subsystem\n"); + pinctrl_init_debugfs(); + return 0; +diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c +index f9ecbebe6442..baf6079962f5 100644 +--- a/drivers/pinctrl/tegra/pinctrl-tegra.c ++++ b/drivers/pinctrl/tegra/pinctrl-tegra.c +@@ -34,13 +34,28 @@ + #define EMMC_DPD_PARKING(x) (x << EMMC_PARKING_BIT) + #define EMMC_PARKING_SET 0x1FFF + ++// #define GPIO_DEBUG ++ ++#ifdef GPIO_DEBUG ++#define deb_info(fmt, ...) printk(KERN_INFO"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_debug(fmt, ...) printk(KERN_DEBUG"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#else ++#define deb_info(fmt, ...) ++#define deb_debug(fmt, ...) ++#endif ++ ++ + static inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg) + { ++ deb_debug("\n"); ++ + return readl(pmx->regs[bank] + reg); + } + + static inline void pmx_writel(struct tegra_pmx *pmx, u32 val, u32 bank, u32 reg) + { ++ deb_debug("\n"); ++ + writel_relaxed(val, pmx->regs[bank] + reg); + /* make sure pinmux register write completed */ + pmx_readl(pmx, bank, reg); +@@ -50,6 +65,8 @@ static int tegra_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) + { + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + ++ // deb_debug("\n"); ++ + return pmx->soc->ngroups; + } + +@@ -58,6 +75,8 @@ static const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + { + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + ++ // deb_debug("\n"); ++ + return pmx->soc->groups[group].name; + } + +@@ -68,20 +87,24 @@ static int tegra_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + { + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + ++ // deb_debug("\n"); ++ + *pins = pmx->soc->groups[group].pins; + *num_pins = pmx->soc->groups[group].npins; + + return 0; + } + +-#ifdef CONFIG_DEBUG_FS ++ #ifdef CONFIG_DEBUG_FS + static void tegra_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned offset) + { ++ // deb_debug("\n"); ++ + seq_printf(s, " %s", dev_name(pctldev->dev)); + } +-#endif ++ #endif + + static const struct cfg_param { + const char *property; +@@ -125,6 +148,8 @@ static int tegra_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct property *prop; + const char *group; + ++ deb_debug("\n"); ++ + ret = of_property_read_string(np, "nvidia,function", &function); + if (ret < 0) { + /* EINVAL=missing, which is fine since it's optional */ +@@ -201,6 +226,8 @@ static int tegra_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np; + int ret; + ++ deb_debug("\n"); ++ + reserved_maps = 0; + *map = NULL; + *num_maps = 0; +@@ -234,6 +261,8 @@ static int tegra_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) + { + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + ++ deb_debug("\n"); ++ + return pmx->soc->nfunctions; + } + +@@ -242,6 +271,8 @@ static const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev, + { + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + ++ // deb_debug("\n"); ++ + return pmx->soc->functions[function].name; + } + +@@ -252,6 +283,8 @@ static int tegra_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, + { + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + ++ // deb_debug("\n"); ++ + *groups = pmx->soc->functions[function].groups; + *num_groups = pmx->soc->functions[function].ngroups; + +@@ -267,6 +300,8 @@ static int tegra_pinctrl_set_mux(struct pinctrl_dev *pctldev, + int i; + u32 val; + ++ // deb_debug("GPIO %s, device %s\n", pmx->dev->init_name); ++ + g = &pmx->soc->groups[group]; + + if (WARN_ON(g->mux_reg < 0)) +@@ -300,6 +335,8 @@ static int tegra_pinctrl_gpio_save_config(struct pinctrl_dev *pctldev, + const unsigned *pins; + int ret; + ++ deb_debug("device %s\n", pmx->dev->init_name,__FILE__); ++ + for (group = 0; group < pmx->soc->ngroups; ++group) { + ret = tegra_pinctrl_get_group_pins(pctldev, group, &pins, &num_pins); + if (ret < 0 || num_pins != 1) +@@ -330,6 +367,8 @@ static int tegra_pinctrl_gpio_restore_config(struct pinctrl_dev *pctldev, + const unsigned *pins; + int ret; + ++ deb_debug("device %s\n", pmx->dev->init_name); ++ + for (group = 0; group < pmx->soc->ngroups; ++group) { + ret = tegra_pinctrl_get_group_pins(pctldev, group, &pins, &num_pins); + if (ret < 0 || num_pins != 1) +@@ -358,6 +397,8 @@ static const struct tegra_pingroup *tegra_pinctrl_get_group(struct pinctrl_dev * + const unsigned int *pins; + int ret; + ++ deb_debug("\n"); ++ + for (group = 0; group < pmx->soc->ngroups; ++group) { + ret = tegra_pinctrl_get_group_pins(pctldev, group, &pins, &num_pins); + if (ret < 0) +@@ -384,6 +425,8 @@ static int tegra_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, + u32 value; + int ret; + ++ deb_debug("device %s\n", pmx->dev->init_name); ++ + ret = tegra_pinctrl_gpio_save_config(pctldev, range, offset); + if (ret) + return ret; +@@ -416,6 +459,8 @@ static void tegra_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) + { ++ deb_debug("\n"); ++ + tegra_pinctrl_gpio_restore_config(pctldev, range, offset); + } + +@@ -425,6 +470,8 @@ static int tegra_pinctrl_gpio_set_input(struct tegra_pmx *pmx, + { + u32 value; + ++ deb_debug("\n"); ++ + if (group->einput_bit < 0) + return 0; + +@@ -449,6 +496,8 @@ static int tegra_pinctrl_gpio_set_tristate(struct tegra_pmx *pmx, + { + u32 value; + ++ deb_debug("\n"); ++ + if (group->tri_bank < 0 || group->tri_reg < 0 || group->tri_bit < 0) + return -EINVAL; + +@@ -472,6 +521,8 @@ static int tegra_pinctrl_gpio_set_direction(struct pinctrl_dev *pctldev, + const struct tegra_pingroup *group; + int ret; + ++ deb_debug("Offset=%d\n", offset); ++ + group = tegra_pinctrl_get_group(pctldev, offset); + if (!group) + return -EINVAL; +@@ -507,6 +558,8 @@ static int tegra_pinconf_reg(struct tegra_pmx *pmx, + bool report_err, + s8 *bank, s32 *reg, s8 *bit, s8 *width) + { ++ deb_debug("\n"); ++ + switch (param) { + case TEGRA_PINCONF_PARAM_PULL: + *bank = g->pupd_bank; +@@ -661,6 +714,7 @@ static int tegra_pinconf_reg(struct tegra_pmx *pmx, + static int tegra_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long *config) + { ++ deb_debug("\n"); + dev_err(pctldev->dev, "pin_config_get op not supported\n"); + return -ENOTSUPP; + } +@@ -669,6 +723,7 @@ static int tegra_pinconf_set(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long *configs, + unsigned num_configs) + { ++ deb_debug("\n"); + dev_err(pctldev->dev, "pin_config_set op not supported\n"); + return -ENOTSUPP; + } +@@ -685,6 +740,8 @@ static int tegra_pinconf_group_get(struct pinctrl_dev *pctldev, + s32 reg; + u32 val, mask; + ++ deb_debug("\n"); ++ + g = &pmx->soc->groups[group]; + + ret = tegra_pinconf_reg(pmx, g, param, true, &bank, ®, &bit, +@@ -718,6 +775,8 @@ static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, + s32 reg; + u32 val, mask; + ++ deb_debug("\n"); ++ + g = &pmx->soc->groups[group]; + + for (i = 0; i < num_configs; i++) { +@@ -765,15 +824,17 @@ static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, + return 0; + } + +-#ifdef CONFIG_DEBUG_FS ++ #ifdef CONFIG_DEBUG_FS + static void tegra_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned offset) + { ++ deb_debug("\n"); + } + + static const char *strip_prefix(const char *s) + { + const char *comma = strchr(s, ','); ++ deb_debug("\n"); + if (!comma) + return s; + +@@ -791,6 +852,8 @@ static void tegra_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, + u32 val; + u8 idx; + ++ deb_debug("\n"); ++ + g = &pmx->soc->groups[group]; + + for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { +@@ -823,6 +886,8 @@ static void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, + const char *pname = "unknown"; + int i; + ++ deb_debug("\n"); ++ + for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { + if (cfg_params[i].param == param) { + pname = cfg_params[i].property; +@@ -832,7 +897,7 @@ static void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, + + seq_printf(s, "%s=%d", strip_prefix(pname), arg); + } +-#endif ++ #endif + + static const struct pinconf_ops tegra_pinconf_ops = { + .pin_config_get = tegra_pinconf_get, +@@ -865,6 +930,8 @@ static void tegra_pinctrl_clear_parked_bits(struct tegra_pmx *pmx) + const struct tegra_pingroup *g; + u32 val; + ++ deb_debug("\n"); ++ + for (i = 0; i < pmx->soc->ngroups; ++i) { + g = &pmx->soc->groups[i]; + if (g->parked_bitmask > 0) { +@@ -891,6 +958,8 @@ static size_t tegra_pinctrl_get_bank_size(struct device *dev, + struct platform_device *pdev = to_platform_device(dev); + struct resource *res; + ++ deb_debug("\n"); ++ + res = platform_get_resource(pdev, IORESOURCE_MEM, bank_id); + + return resource_size(res) / 4; +@@ -904,6 +973,8 @@ static int tegra_pinctrl_suspend(struct device *dev) + size_t bank_size; + unsigned int i, k; + ++ deb_debug("\n"); ++ + for (i = 0; i < pmx->nbanks; i++) { + bank_size = tegra_pinctrl_get_bank_size(dev, i); + regs = pmx->regs[i]; +@@ -922,6 +993,8 @@ static int tegra_pinctrl_resume(struct device *dev) + size_t bank_size; + unsigned int i, k; + ++ deb_debug("\n"); ++ + for (i = 0; i < pmx->nbanks; i++) { + bank_size = tegra_pinctrl_get_bank_size(dev, i); + regs = pmx->regs[i]; +@@ -961,6 +1034,8 @@ static bool tegra_pinctrl_gpio_node_has_range(struct tegra_pmx *pmx) + struct device_node *np; + bool has_prop = false; + ++ deb_debug("\n"); ++ + if (of_property_read_bool(dev->of_node, "#gpio-range-cells")) + return true; + +@@ -985,6 +1060,8 @@ int tegra_pinctrl_probe(struct platform_device *pdev, + int fn, gn, gfn; + unsigned long backup_regs_size = 0; + ++ deb_debug("\n"); ++ + pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); + if (!pmx) + return -ENOMEM; +diff --git a/drivers/pinctrl/tegra/pinctrl-tegra114.c b/drivers/pinctrl/tegra/pinctrl-tegra114.c +index 135635f4e897..a5da4ec197dc 100644 +--- a/drivers/pinctrl/tegra/pinctrl-tegra114.c ++++ b/drivers/pinctrl/tegra/pinctrl-tegra114.c +@@ -1845,8 +1845,26 @@ static const struct tegra_pinctrl_soc_data tegra114_pinctrl = { + .drvtype_in_mux = false, + }; + ++// #define GPIO_DEBUG ++// #define GPIO_DEBUG_VERBOSE ++ ++#ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++#else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ + static int tegra114_pinctrl_probe(struct platform_device *pdev) + { ++ deb_debug("\n"); + return tegra_pinctrl_probe(pdev, &tegra114_pinctrl); + } + +@@ -1865,6 +1883,7 @@ static struct platform_driver tegra114_pinctrl_driver = { + + static int __init tegra114_pinctrl_init(void) + { ++ deb_debug("\n"); + return platform_driver_register(&tegra114_pinctrl_driver); + } + arch_initcall(tegra114_pinctrl_init); +diff --git a/drivers/pinctrl/tegra/pinctrl-tegra124.c b/drivers/pinctrl/tegra/pinctrl-tegra124.c +index cfc75ca9ae2b..6e5295fdd50e 100644 +--- a/drivers/pinctrl/tegra/pinctrl-tegra124.c ++++ b/drivers/pinctrl/tegra/pinctrl-tegra124.c +@@ -2057,8 +2057,26 @@ static const struct tegra_pinctrl_soc_data tegra124_pinctrl = { + .drvtype_in_mux = false, + }; + ++// #define GPIO_DEBUG ++// #define GPIO_DEBUG_VERBOSE ++ ++#ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++#else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ + static int tegra124_pinctrl_probe(struct platform_device *pdev) + { ++ deb_debug("\n"); + return tegra_pinctrl_probe(pdev, &tegra124_pinctrl); + } + +@@ -2077,6 +2095,7 @@ static struct platform_driver tegra124_pinctrl_driver = { + + static int __init tegra124_pinctrl_init(void) + { ++ deb_debug("\n"); + return platform_driver_register(&tegra124_pinctrl_driver); + } + arch_initcall(tegra124_pinctrl_init); +diff --git a/drivers/pinctrl/tegra/pinctrl-tegra186.c b/drivers/pinctrl/tegra/pinctrl-tegra186.c +index d78447c55527..7df0e6496fad 100644 +--- a/drivers/pinctrl/tegra/pinctrl-tegra186.c ++++ b/drivers/pinctrl/tegra/pinctrl-tegra186.c +@@ -918,8 +918,26 @@ static const struct tegra_pinctrl_soc_data tegra186_pinctrl = { + .sfsel_in_mux = true, + }; + ++// #define GPIO_DEBUG ++// #define GPIO_DEBUG_VERBOSE ++ ++#ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++#else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ + static int tegra186_pinctrl_probe(struct platform_device *pdev) + { ++ deb_debug("\n"); + return tegra_pinctrl_probe(pdev, &tegra186_pinctrl); + } + +@@ -940,12 +958,14 @@ static struct platform_driver tegra186_pinctrl_driver = { + + static int __init tegra186_pinctrl_init(void) + { ++ deb_debug("\n"); + return platform_driver_register(&tegra186_pinctrl_driver); + } + postcore_initcall_sync(tegra186_pinctrl_init); + + static void __exit tegra186_pinctrl_exit(void) + { ++ deb_debug("\n"); + platform_driver_unregister(&tegra186_pinctrl_driver); + } + module_exit(tegra186_pinctrl_exit); +diff --git a/drivers/pinctrl/tegra/pinctrl-tegra194.c b/drivers/pinctrl/tegra/pinctrl-tegra194.c +index 5c7fa1f1c45f..cfb1fb2e0c2e 100644 +--- a/drivers/pinctrl/tegra/pinctrl-tegra194.c ++++ b/drivers/pinctrl/tegra/pinctrl-tegra194.c +@@ -1872,8 +1872,27 @@ static const struct tegra_pinctrl_soc_data tegra194_pinctrl = { + .sfsel_in_mux = true, + }; + ++// #define GPIO_DEBUG ++// #define GPIO_DEBUG_VERBOSE ++ ++#ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++#else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ ++ + static int tegra194_pinctrl_probe(struct platform_device *pdev) + { ++ deb_debug("\n"); + return tegra_pinctrl_probe(pdev, &tegra194_pinctrl); + } + +@@ -1892,6 +1911,7 @@ static struct platform_driver tegra194_pinctrl_driver = { + + static int __init tegra194_pinctrl_init(void) + { ++ deb_debug("\n"); + return platform_driver_register(&tegra194_pinctrl_driver); + } + arch_initcall(tegra194_pinctrl_init); +diff --git a/drivers/pinctrl/tegra/pinctrl-tegra20.c b/drivers/pinctrl/tegra/pinctrl-tegra20.c +index cd605272c068..42f1fe4eb064 100644 +--- a/drivers/pinctrl/tegra/pinctrl-tegra20.c ++++ b/drivers/pinctrl/tegra/pinctrl-tegra20.c +@@ -2236,6 +2236,23 @@ static const char *cdev2_parents[] = { + "dev2_osc_div", "hclk", "pclk", "pll_p_out4", + }; + ++// #define GPIO_DEBUG ++// #define GPIO_DEBUG_VERBOSE ++ ++#ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++#else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ + static void tegra20_pinctrl_register_clock_muxes(struct platform_device *pdev) + { + struct tegra_pmx *pmx = platform_get_drvdata(pdev); +@@ -2249,6 +2266,7 @@ static void tegra20_pinctrl_register_clock_muxes(struct platform_device *pdev) + + static int tegra20_pinctrl_probe(struct platform_device *pdev) + { ++ deb_debug("\n"); + int err; + + err = tegra_pinctrl_probe(pdev, &tegra20_pinctrl); +@@ -2275,6 +2293,7 @@ static struct platform_driver tegra20_pinctrl_driver = { + + static int __init tegra20_pinctrl_init(void) + { ++ deb_debug("\n"); + return platform_driver_register(&tegra20_pinctrl_driver); + } + arch_initcall(tegra20_pinctrl_init); +diff --git a/drivers/pinctrl/tegra/pinctrl-tegra210.c b/drivers/pinctrl/tegra/pinctrl-tegra210.c +index 01f200a4c789..12193bd5ffb1 100644 +--- a/drivers/pinctrl/tegra/pinctrl-tegra210.c ++++ b/drivers/pinctrl/tegra/pinctrl-tegra210.c +@@ -1745,12 +1745,16 @@ static const struct tegra_pinctrl_soc_data tegra210_pinctrl = { + .drvtype_in_mux = true, + }; + ++// #define GPIO_DEBUG ++ + static int tegra210_pinctrl_probe(struct platform_device *pdev) + { + const struct tegra210_pinctrl_soc *soc; + struct tegra_pingroup *g; + int i; + ++ printk(KERN_DEBUG "GPIO %s -- file %s", __func__, __FILE__); ++ + soc = of_device_get_match_data(&pdev->dev); + if (soc->lpdr_support) { + for (i = 0; i < tegra210_pinctrl.ngroups; ++i) { +@@ -1793,6 +1797,7 @@ static struct platform_driver tegra210_pinctrl_driver = { + + static int __init tegra210_pinctrl_init(void) + { ++ printk(KERN_DEBUG "GPIO %s -- file %s", __func__, __FILE__); + return platform_driver_register(&tegra210_pinctrl_driver); + } + arch_initcall(tegra210_pinctrl_init); +diff --git a/drivers/pinctrl/tegra/pinctrl-tegra30.c b/drivers/pinctrl/tegra/pinctrl-tegra30.c +index 60e087e5b7f5..eacae723cf11 100644 +--- a/drivers/pinctrl/tegra/pinctrl-tegra30.c ++++ b/drivers/pinctrl/tegra/pinctrl-tegra30.c +@@ -2480,8 +2480,26 @@ static const struct tegra_pinctrl_soc_data tegra30_pinctrl = { + .drvtype_in_mux = false, + }; + ++// #define GPIO_DEBUG ++// #define GPIO_DEBUG_VERBOSE ++ ++#ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++#else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ + static int tegra30_pinctrl_probe(struct platform_device *pdev) + { ++ deb_debug("\n"); + return tegra_pinctrl_probe(pdev, &tegra30_pinctrl); + } + +@@ -2500,6 +2518,7 @@ static struct platform_driver tegra30_pinctrl_driver = { + + static int __init tegra30_pinctrl_init(void) + { ++ deb_debug("\n"); + return platform_driver_register(&tegra30_pinctrl_driver); + } + arch_initcall(tegra30_pinctrl_init); +diff --git a/include/linux/gpio.h b/include/linux/gpio.h +index 68193b32eb73..44a827979aad 100644 +--- a/include/linux/gpio.h ++++ b/include/linux/gpio.h +@@ -56,7 +56,7 @@ struct gpio { + #ifdef CONFIG_GPIOLIB + + #ifdef CONFIG_ARCH_HAVE_CUSTOM_GPIO_H +-#include ++ + #else + + #include diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch new file mode 100644 index 000000000..a9940b3c2 --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -0,0 +1,1347 @@ +diff --git a/drivers/Makefile b/drivers/Makefile +index d3500440b928..67f63d873ae8 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -191,3 +191,8 @@ obj-$(CONFIG_GNSS) += gnss/ + obj-$(CONFIG_INTERCONNECT) += interconnect/ + obj-$(CONFIG_COUNTER) += counter/ + obj-$(CONFIG_MOST) += most/ ++# ++# ++# ++obj-y += gpio-host-proxy/ ++obj-y += gpio-guest-proxy/ +diff --git a/drivers/Kconfig b/drivers/Kconfig +index dcecc9f6e33f..ed7d58d68ba6 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -235,4 +235,10 @@ source "drivers/interconnect/Kconfig" + source "drivers/counter/Kconfig" + + source "drivers/most/Kconfig" ++ ++if ARCH_TEGRA ++source "drivers/gpio-host-proxy/Kconfig" ++source "drivers/gpio-guest-proxy/Kconfig" ++endif ++ + endmenu +diff --git a/drivers/gpio-guest-proxy/Kconfig b/drivers/gpio-guest-proxy/Kconfig +new file mode 100644 +index 0000000..e25a19a +--- /dev/null ++++ b/drivers/gpio-guest-proxy/Kconfig +@@ -0,0 +1,11 @@ ++config TEGRA_GPIO_GUEST_PROXY ++ depends on GPIO_TEGRA && GPIO_TEGRA186 ++ bool "Tegra GPIO guest proxy driver" ++ help ++ The Tegra GPIO guest proxy driver, virtualise GPIO __iomem initalised by host, ++ allowing the guest to access host GPIO. ++ ++ Say Y here to enable this driver and to compile this driver as a module, ++ choose M here. If unsure, say N ++ ++ +diff --git a/drivers/gpio-guest-proxy/Makefile b/drivers/gpio-guest-proxy/Makefile +new file mode 100644 +index 0000000..2580e02 +--- /dev/null ++++ b/drivers/gpio-guest-proxy/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_TEGRA_GPIO_GUEST_PROXY) += gpio-guest-proxy.o +diff --git a/drivers/gpio-guest-proxy/gpio-guest-proxy.c b/drivers/gpio-guest-proxy/gpio-guest-proxy.c +new file mode 100644 +index 0000000..074cfd4 +--- /dev/null ++++ b/drivers/gpio-guest-proxy/gpio-guest-proxy.c +@@ -0,0 +1,531 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/** ++ * NVIDIA GPIO Guest Proxy Kernel Module ++ * (c) 2023 Unikie, Oy ++ * (c) 2023 Kim Sandstrom kim.sandstrom@unikie.com ++ * ++ **/ ++ ++#include // Core header for modules. ++#include // Supports driver model. ++#include // Kernel header for convenient functions. ++#include // File-system support. ++#include // User access copy function support. ++#include ++#include ++#include ++#include ++#include ++#include "../gpio-host-proxy/gpio-host-proxy.h" ++#include ++#include ++#include ++#include ++#include ++#include ++ ++// we need a workaround for this -- relevant data copied to gpio-host-proxy.h ++// #include "$(abs_srctree)/drivers/gpio/gpiolib.h" ++ ++ ++ ++#define DEVICE_NAME "gpio-guest" // Device name. ++#define CLASS_NAME "char" ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Kim Sandstrom"); ++MODULE_DESCRIPTION("NVidia GPIO Guest Proxy Kernel Module"); ++MODULE_VERSION("0.0"); ++ ++#define GPIO_DEBUG ++#define GPIO_DEBUG_VERBOSE // also activates deb_verbose commands ++ ++#ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\'" fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\'" fmt, __func__ , __FILE__, ##__VA_ARGS__) ++ #define deb_error(fmt, ...) printk(KERN_ERR "GPIO func \'%s\' in file \'%s\'" fmt, __func__ , __FILE__, ##__VA_ARGS__) ++#else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++ #define deb_error(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++ extern void hexDump ( ++ const char * desc, ++ const void * addr, ++ const int len ++ ); ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ ++#define MEM_SIZE 0x0600 // size of passthrough memory (mem_iova) -- larger than needed ++static volatile void __iomem *mem_iova = NULL; ++ ++extern struct gpio_chip *find_chip_by_name(const char *); ++extern struct gpio_chip *find_chip_by_id(int); ++extern const char **tegra_chiplabel; ++ ++extern int gpio_outloud; ++extern uint64_t gpio_vpa; ++ ++/* functions redirected to guest-proxy from gpio-tegra186.c ++ * from setup of gpio_chip in tegra186_gpio_probe ++ */ ++ ++/* guest_chardev_transfer ++ * a helper function to transfer between guest and host using /dev/gpio-host ++ * mgs: is a pointer to data -- in practice tegra_gpio_pt and tegra_gpio_pt_extended structs ++ * generic return: poiter to return data, may be NULL if we do not expect a return value ++ */ ++void guest_chardev_transfer(void *msg, int msg_len, int *generic_return) ++{ ++ unsigned char *io_buffer; ++ ++ deb_debug("\n"); ++ ++ // Copy msg, to io_buffer ++ io_buffer = kmalloc(msg_len, GFP_KERNEL); ++ memset(io_buffer, 0, msg_len); ++ memcpy(io_buffer, msg, msg_len); ++ ++ #ifdef GPIO_DEBUG_VERBOSE ++ hexDump("msg", &msg, msg_len); ++ deb_verbose("msg signal is: %c\n", *(char *)msg); ++ #endif ++ ++ // Execute the request by copying the io_buffer ++ memcpy_toio(mem_iova, io_buffer, msg_len); ++ ++ // Read response to io_buffer ++ memcpy_fromio(io_buffer, mem_iova, sizeof(*generic_return)); ++ ++ // check if we expect a return value ++ if(generic_return) { ++ // Copy reply to io_buffer ++ memcpy(generic_return, io_buffer, sizeof(*generic_return)); ++ deb_verbose("return value is copied %d\n", *generic_return); ++ } ++ ++ kfree(io_buffer); ++} ++ ++int gpiochip_generic_request_redirect(struct gpio_chip *chip, unsigned offset) { ++ int ret = 0; ++ struct tegra_gpio_pt msg; ++ ++ msg.signal = GPIO_REQ; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = 0; ++ msg.offset = offset; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ return ret; ++} ++ ++void gpiochip_generic_free_redirect(struct gpio_chip *chip, unsigned offset) { ++ struct tegra_gpio_pt msg; ++ ++ msg.signal = GPIO_FREE; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = 0; ++ msg.offset = offset; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), NULL); ++} ++ ++int tegra186_gpio_get_direction_redirect(struct gpio_chip *chip, ++ unsigned int offset) { ++ int ret = 0; ++ struct tegra_gpio_pt msg; ++ ++ msg.signal = GPIO_GET_DIR; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = 0; ++ msg.offset = offset; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ return ret; ++} ++ ++int tegra186_gpio_direction_input_redirect(struct gpio_chip *chip, ++ unsigned int offset) { ++ int ret = 0; ++ struct tegra_gpio_pt msg; ++ ++ msg.signal = GPIO_SET_IN; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = 0; ++ msg.offset = offset; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ return ret; ++} ++ ++int tegra186_gpio_direction_output_redirect(struct gpio_chip *chip, ++ unsigned int offset, int level) { ++ int ret = 0; ++ struct tegra_gpio_pt msg; ++ ++ msg.signal = GPIO_SET_OUT; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = level; ++ msg.offset = offset; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ return ret; ++} ++ ++int tegra186_gpio_get_redirect(struct gpio_chip *chip, unsigned int offset) { ++ int ret = 0; ++ struct tegra_gpio_pt msg; ++ ++ msg.signal = GPIO_GET; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = 0; ++ msg.offset = offset; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ return ret; ++} ++ ++void tegra186_gpio_set_redirect(struct gpio_chip *chip, unsigned int offset, ++ int level) { ++ struct tegra_gpio_pt msg; ++ ++ msg.signal = GPIO_SET; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = level; ++ msg.offset = offset; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), NULL); ++} ++ ++void tegra186_gpio_set_by_name_redirect(const char *name, unsigned int offset, ++ int level) { ++ struct tegra_gpio_pt msg; ++ struct gpio_chip *chip = find_chip_by_name(name); ++ msg.signal = GPIO_SET_BY_NAME; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = level; ++ msg.offset = offset; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), NULL); ++} ++ ++int tegra186_gpio_set_config_redirect(struct gpio_chip *chip, ++ unsigned int offset, ++ unsigned long config) { ++ int ret = 0; ++ struct tegra_gpio_pt msg; ++ ++ msg.signal = GPIO_CONFIG; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = 0; ++ msg.offset = offset; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ return ret; ++} ++ ++int tegra_gpio_timestamp_control_redirect(struct gpio_chip *chip, unsigned offset, ++ int enable) { ++ int ret = 0; ++ struct tegra_gpio_pt msg; ++ ++ msg.signal = GPIO_TIMESTAMP_CTRL; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = 0; ++ msg.offset = offset; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ return ret; ++} ++ ++int tegra_gpio_timestamp_read_redirect(struct gpio_chip *chip, unsigned offset, ++ u64 *ts) { ++ int ret = 0; ++ struct tegra_gpio_pt msg; ++ ++ msg.signal = GPIO_TIMESTAMP_READ; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = 0; ++ msg.offset = offset; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ return ret; ++} ++ ++int tegra_gpio_suspend_configure_redirect(struct gpio_chip *chip, unsigned offset, ++ enum gpiod_flags dflags) { ++ int ret = 0; ++ struct tegra_gpio_pt msg; ++ ++ msg.signal = GPIO_SUSPEND_CONF; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = 0; ++ msg.offset = offset; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ return ret; ++} ++ ++// TODO, this probably does not work because action parameters should be in gpio_chip struct, but guest did not copy the over the passthrough interface ++int tegra186_gpio_add_pin_ranges_redirect(struct gpio_chip *chip) { ++ int ret = 0; ++ struct tegra_gpio_pt msg; ++ ++ msg.signal = GPIO_ADD_PINRANGES; ++ msg.chipnum = chip->gpiodev->id; ++ msg.level = 0; ++ msg.offset = 0; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ return ret; ++} ++ ++/* ++// export of redirection functions ++EXPORT_SYMBOL_GPL(gpiochip_generic_request_redirect); ++EXPORT_SYMBOL_GPL(gpiochip_generic_free_redirect); ++EXPORT_SYMBOL_GPL(tegra186_gpio_get_direction_redirect); ++EXPORT_SYMBOL_GPL(tegra186_gpio_direction_input_redirect); ++EXPORT_SYMBOL_GPL(tegra186_gpio_direction_output_redirect); ++EXPORT_SYMBOL_GPL(tegra186_gpio_get_redirect); ++EXPORT_SYMBOL_GPL(tegra186_gpio_set_redirect); ++EXPORT_SYMBOL_GPL(tegra186_gpio_set_by_name_redirect); ++EXPORT_SYMBOL_GPL(tegra186_gpio_set_config_redirect); ++EXPORT_SYMBOL_GPL(tegra_gpio_timestamp_control_redirect); ++EXPORT_SYMBOL_GPL(tegra_gpio_timestamp_read_redirect); ++EXPORT_SYMBOL_GPL(tegra_gpio_suspend_configure_redirect); ++EXPORT_SYMBOL_GPL(tegra186_gpio_add_pin_ranges_redirect); ++*/ ++ ++// unpreserve_all_tegrachips also does unhooking ? ++extern void unpreserve_all_tegrachips(void); ++struct gpio_chip * find_chip_by_id(int id); ++ ++/** ++ * Important variables that store data and keep track of relevant information. ++ */ ++static int major_number; ++ ++static struct class *gpio_guest_proxy_class = NULL; ///< The device-driver class struct pointer ++static struct device *gpio_guest_proxy_device = NULL; ///< The device-driver device struct pointer ++ ++/** ++ * Prototype functions for file operations. ++ */ ++static int open(struct inode *, struct file *); ++static int close(struct inode *, struct file *); ++static ssize_t read(struct file *, char *, size_t, loff_t *); ++static ssize_t write(struct file *, const char *, size_t, loff_t *); ++ ++/** ++ * File operations structure and the functions it points to. ++ */ ++static struct file_operations fops = ++ { ++ .owner = THIS_MODULE, ++ .open = open, ++ .release = close, ++ .read = read, ++ .write = write ++ }; ++ ++/** ++ * Initializes module at installation ++ */ ++int tegra_gpio_guest_init(struct gpio_chip *gpio) ++{ ++ deb_info("installing module."); ++ ++ deb_info("gpio_vpa: 0x%llx", gpio_vpa); ++ ++ if(!gpio_vpa){ ++ pr_err("Failed, gpio_vpa not defined"); ++ } ++ ++ // Allocate a major number for the device. ++ major_number = register_chrdev(0, DEVICE_NAME, &fops); ++ if (major_number < 0) ++ { ++ pr_err("could not register number."); ++ return major_number; ++ } ++ deb_info("registered correctly with major number %d", major_number); ++ ++ // Register the device class ++ gpio_guest_proxy_class = class_create(THIS_MODULE, CLASS_NAME); ++ if (IS_ERR(gpio_guest_proxy_class)) ++ { // Check for error and clean up if there is ++ unregister_chrdev(major_number, DEVICE_NAME); ++ deb_error("Failed to register device class\n"); ++ return PTR_ERR(gpio_guest_proxy_class); // Correct way to return an error on a pointer ++ } ++ deb_info("device class registered correctly\n"); ++ ++ // Register the device driver ++ gpio_guest_proxy_device = device_create(gpio_guest_proxy_class, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME); ++ if (IS_ERR(gpio_guest_proxy_device)) ++ { // Clean up if there is an error ++ class_destroy(gpio_guest_proxy_class); ++ unregister_chrdev(major_number, DEVICE_NAME); ++ deb_error("Failed to create the device\n"); ++ return PTR_ERR(gpio_guest_proxy_device); ++ } ++ deb_info("device class created correctly\n"); // Made it! device was initialized ++ ++ // map iomem ++ mem_iova = ioremap(gpio_vpa, MEM_SIZE); ++ ++ if (!mem_iova) { ++ deb_error("ioremap failed\n"); ++ return -ENOMEM; ++ } ++ ++ deb_info("gpio_vpa: 0x%llX, mem_iova: %p\n", gpio_vpa, mem_iova); ++ ++ // gpio_hook is called by preserve_tegrachip() in gpio_tegra186.c -- don't call it here ++ // gpio_hook() ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL_GPL(tegra_gpio_guest_init); ++ ++/* ++ * Removes module, sends appropriate message to kernel ++ */ ++void tegra_gpio_guest_cleanup(void) ++{ ++ deb_info("removing module.\n"); ++ ++ // unmap iomem ++ iounmap((void __iomem*)gpio_vpa); ++ ++ // gpio_unhook is called by unpreserve_all_tegrachips() ++ // gpio_unhook() ++ // clean up shared memory with stock driver and unhook all functions ++ unpreserve_all_tegrachips(); ++ ++ device_destroy(gpio_guest_proxy_class, MKDEV(major_number, 0)); // remove the device ++ class_unregister(gpio_guest_proxy_class); // unregister the device class ++ class_destroy(gpio_guest_proxy_class); // remove the device class ++ unregister_chrdev(major_number, DEVICE_NAME); // unregister the major number ++ deb_info("Goodbye from the LKM!\n"); ++ unregister_chrdev(major_number, DEVICE_NAME); ++ return; ++} ++ ++ ++ ++ ++/* ++ * Opens device module, sends appropriate message to kernel ++ */ ++static int open(struct inode *inodep, struct file *filep) ++{ ++ deb_info("device opened.\n"); ++ gpio_outloud = 1; ++ return 0; ++} ++ ++/* ++ * Closes device module, sends appropriate message to kernel ++ */ ++static int close(struct inode *inodep, struct file *filep) ++{ ++ deb_info("device closed.\n"); ++ gpio_outloud = 0; ++ return 0; ++} ++ ++/* ++ * Reads from device, displays in userspace, and deletes the read data ++ */ ++static ssize_t read(struct file *filep, char *buffer, size_t len, loff_t *offset) ++{ ++ deb_info("read stub"); ++ return 0; ++} ++ ++/* ++ * Writes to the device ++ */ ++ ++static ssize_t write(struct file *filep, const char *buffer, size_t len, loff_t *offset) ++{ ++ // tint i = 0; ++ unsigned int ret; ++ unsigned long int ret_l; ++ // struct tegra_gpio_pt *kbuf = NULL; ++ // static struct file *file; ++ // static struct inode *inode = NULL; ++ // struct gpio_chip *chip; ++ // unsigned int gpio_number; ++ char *return_buffer = (char *)buffer; ++ char *read_buffer = (char *)buffer; ++ ++ deb_info("wants to write %zu bytes", len); ++ ++ if (len > 65535) { ++ pr_err("count %zu exceeds max # of bytes allowed, aborting write", len); ++ return -EINVAL; ++ } ++ ++ if(len != sizeof(struct tegra_gpio_pt) || len != sizeof(tegra_gpio_pt_extended) ) { ++ pr_err("Illegal data length %ld", len); ++ return -EFAULT; ++ } ++ ++ /* ++ kbuf = kmalloc(len, GFP_KERNEL); ++ if ( !kbuf ) { ++ pr_err("memory allocation failed"); ++ return -ENOMEM; ++ } ++ ++ memset(kbuf, 0, len); ++ ++ // Copy header ++ if (copy_from_user(kbuf, read_buffer, sizeof(struct tegra_gpio_pt))) { ++ pr_err("copy_from_user failed"); ++ kfree(kbuf); ++ return -ENOMEM; ++ } ++ ++ // copied user parameters ++ deb_debug("GPIO chardev parameters: Chip %d, Offset %d, Level %d", kbuf->chipnum, kbuf->offset, kbuf->level); ++ */ ++ ++ // send data "as is" to host ++ memcpy_toio(mem_iova, read_buffer, len); ++ // TODO handle return messages from host ++ ++ goto end; ++ goto retlong; ++ goto retval; ++ ++ retlong: ++ if ( copy_to_user(return_buffer, &ret_l, sizeof(ret_l)) ) { ++ pr_err("GPIO, copying int user return value failed"); ++ //kfree(kbuf); ++ return -EFAULT; ++ } ++ goto end; ++ ++ retval: ++ if ( copy_to_user(return_buffer, &ret, sizeof(ret)) ) { ++ pr_err("GPIO, copying long int user return value failed"); ++ //kfree(kbuf); ++ return -EFAULT; ++ } ++ goto end; ++ ++ end: ++ //kfree(kbuf); ++ return len; ++} +diff --git a/drivers/gpio-host-proxy/Kconfig b/drivers/gpio-host-proxy/Kconfig +new file mode 100644 +index 0000000..3047dab +--- /dev/null ++++ b/drivers/gpio-host-proxy/Kconfig +@@ -0,0 +1,10 @@ ++config TEGRA_GPIO_HOST_PROXY ++ depends on GPIO_TEGRA && GPIO_TEGRA186 ++ bool "Tegra GPIO host proxy support" ++ help ++ Exposes the GPIO capabilities __iomem to the user space, in order to support the ++ GPIO passthrough to virtual machines. ++ ++ Say Y here to enable this driver and to compile this driver as a module, ++ choose M here. If unsure, say N ++ +diff --git a/drivers/gpio-host-proxy/Makefile b/drivers/gpio-host-proxy/Makefile +new file mode 100644 +index 0000000..c2e0184 +--- /dev/null ++++ b/drivers/gpio-host-proxy/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_TEGRA_GPIO_HOST_PROXY) += gpio-host-proxy.o +diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.c b/drivers/gpio-host-proxy/gpio-host-proxy.c +new file mode 100644 +index 0000000..dc4cee6 +--- /dev/null ++++ b/drivers/gpio-host-proxy/gpio-host-proxy.c +@@ -0,0 +1,644 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/** ++ * NVIDIA GPIO Guest Proxy Kernel Module ++ * (c) 2023 Unikie, Oy ++ * (c) 2023 Kim Sandstrom kim.sandstrom@unikie.com ++ * ++ **/ ++#include // Core header for modules. ++#include // Supports driver model. ++#include // Kernel header for convenient functions. ++#include // File-system support. ++#include // User access copy function support. ++#include ++//#include ++#include ++#include ++#include "../gpio-host-proxy/gpio-host-proxy.h" ++#include ++#include ++#include ++#include ++#include ++ ++#define DEVICE_NAME "gpio-host" // Device name. ++#define CLASS_NAME "chardrv" // < The device class -- this is a character device driver ++ ++MODULE_LICENSE("GPL\n"); ///< The license type -- this affects available functionality ++MODULE_AUTHOR("Kim Sandström\n"); ///< The author -- visible when you use modinfo ++MODULE_DESCRIPTION("NVidia GPIO Host Proxy Kernel Module\n"); ///< The description -- see modinfo ++MODULE_VERSION("0.0\n"); ///< A version number to inform users ++ ++#define GPIO_DEBUG ++#define GPIO_DEBUG_VERBOSE // also activates deb_verbose commands ++ ++#ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\'" fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\'" fmt, __func__, __FILE__, ##__VA_ARGS__) ++#else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ ++extern struct gpio_chip *find_chip_by_name(const char *); ++extern struct gpio_chip *find_chip_by_id(int); ++const char *tegra_chiplabel[2] = {TEGRA_GPIO_LABEL,TEGRA_GPIO_AON_LABEL}; ++EXPORT_SYMBOL_GPL(tegra_chiplabel); ++ ++/** ++ * Important variables that store data and keep track of relevant information. ++ */ ++static int major_number; ++ ++static struct class *gpio_host_proxy_class = NULL; ///< The device-driver class struct pointer ++static struct device *gpio_host_proxy_device = NULL; ///< The device-driver device struct pointer ++ ++/** ++ * Prototype functions for file operations. ++ */ ++static int open(struct inode *, struct file *); ++static int close(struct inode *, struct file *); ++static ssize_t read(struct file *, char *, size_t, loff_t *); ++static ssize_t write(struct file *, const char *, size_t, loff_t *); ++ ++/** ++ * File operations structure and the functions it points to. ++ */ ++static struct file_operations fops = ++ { ++ .owner = THIS_MODULE, ++ .open = open, ++ .release = close, ++ .read = read, ++ .write = write ++ }; ++ ++// GPIO allowed resources structure ++// static struct gpio_allowed_res gpio_ares; ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ // Usage: ++ // hexDump(desc, addr, len, perLine); ++ // desc: if non-NULL, printed as a description before hex dump. ++ // addr: the address to start dumping from. ++ // len: the number of bytes to dump. ++ // perLine: number of bytes on each output line. ++ void hexDump ( ++ const char * desc, ++ const void * addr, ++ const int len ++ ) { ++ // Silently ignore silly per-line values. ++ ++ int i; ++ unsigned char buff[17]; ++ unsigned char out_buff[4000]; ++ unsigned char *p_out_buff = out_buff; ++ const unsigned char * pc = (const unsigned char *)addr; ++ ++ ++ ++ // Output description if given. ++ ++ if (desc != NULL) printk ("%s:\n", desc); ++ ++ // Length checks. ++ ++ if (len == 0) { ++ printk(DEVICE_NAME ": ZERO LENGTH\n"); ++ return; ++ } ++ if (len < 0) { ++ printk(DEVICE_NAME ": NEGATIVE LENGTH: %d\n", len); ++ return; ++ } ++ ++ if(len > 400){ ++ printk(DEVICE_NAME ": VERY LONG: %d\n", len); ++ return; ++ } ++ ++ // Process every byte of hexDump data. ++ ++ for (i = 0; i < len; i++) { ++ // Multiple of perLine means new or first line (with line offset). ++ ++ if ((i % 16) == 0) { ++ // Only print previous-line ASCII buffer for lines beyond first. ++ ++ if (i != 0) { ++ p_out_buff += sprintf (p_out_buff, " %s\n", buff); ++ } ++ // Output the offset of current line. ++ ++ p_out_buff += sprintf (p_out_buff," %04x ", i); ++ } ++ ++ // Now the hex code for the specific character. ++ ++ p_out_buff += sprintf (p_out_buff, " %02x", pc[i]); ++ ++ // And buffer a printable ASCII character for later. ++ ++ if ((pc[i] < 0x20) || (pc[i] > 0x7e)) // isprint() may be better. ++ buff[i % 16] = '.'; ++ else ++ buff[i % 16] = pc[i]; ++ buff[(i % 16) + 1] = '\0'; ++ } ++ ++ // Pad out last line if not exactly perLine characters. ++ ++ while ((i % 16) != 0) { ++ p_out_buff += sprintf (p_out_buff, " "); ++ i++; ++ } ++ ++ // And print the final ASCII buffer. ++ ++ p_out_buff += sprintf (p_out_buff, " %s\n", buff); ++ ++ printk(DEVICE_NAME ": %s", out_buff); ++ } ++ EXPORT_SYMBOL_GPL(hexDump); ++#else ++ #define hexDump(...) ++#endif ++ ++/** ++ * Initializes module at installation ++ */ ++static int gpio_host_proxy_probe(struct platform_device *pdev) ++{ ++// int i; ++ deb_info("installing module.\n"); ++ ++// ********************* ++// start of TODO clocks and resets -- this commect section is probably not valid ++ ++// // Read allowed clocks and reset from the device tree ++// // if clocks or resets are not defined, not initialize the module ++// gpio_ares.clocks_size = of_property_read_variable_u32_array(pdev->dev.of_node, ++// "allowed-clocks", gpio_ares.clock, 0, GPIO_HOST_MAX_CLOCKS_SIZE); ++// ++// if(gpio_ares.clocks_size <= 0){ ++// pr_err("No allowed clocks defined\n"); ++// return EINVAL; ++// } ++// ++// deb_info("gpio_ares.clocks_size: %d", gpio_ares.clocks_size); ++// for (i = 0; i < gpio_ares.clocks_size; i++) { ++// deb_info("gpio_ares.clock %d", gpio_ares.clock[i]); ++// } ++// ++// gpio_ares.resets_size = of_property_read_variable_u32_array(pdev->dev.of_node, ++// "allowed-resets", gpio_ares.reset, 0, GPIO_HOST_MAX_RESETS_SIZE); ++// ++// if(gpio_ares.resets_size <= 0){ ++// pr_err("No allowed resets defined\n"); ++// return EINVAL; ++// } ++// ++// deb_info("gpio_ares.resets_size: %d", gpio_ares.resets_size); ++// for (i = 0; i < gpio_ares.resets_size; i++) { ++// deb_info("gpio_ares.reset %d", gpio_ares.reset[i]); ++// } ++// end of TODO clocks and resets ++// ********************* ++ ++ // Allocate a major number for the device. ++ major_number = register_chrdev(0, DEVICE_NAME, &fops); ++ if (major_number < 0) ++ { ++ pr_err("could not register number.\n"); ++ return major_number; ++ } ++ deb_info("registered correctly with major number %d", major_number); ++ ++ // Register the device class ++ gpio_host_proxy_class = class_create(THIS_MODULE, CLASS_NAME); ++ if (IS_ERR(gpio_host_proxy_class)) ++ { // Check for error and clean up if there is ++ unregister_chrdev(major_number, DEVICE_NAME); ++ pr_err("Failed to register device class\n"); ++ return PTR_ERR(gpio_host_proxy_class); // Correct way to return an error on a pointer ++ } ++ deb_info("device class registered correctly\n"); ++ ++ // Register the device driver ++ gpio_host_proxy_device = device_create(gpio_host_proxy_class, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME); ++ if (IS_ERR(gpio_host_proxy_device)) ++ { // Clean up if there is an error ++ class_destroy(gpio_host_proxy_class); ++ unregister_chrdev(major_number, DEVICE_NAME); ++ pr_err("Failed to create the device\n"); ++ return PTR_ERR(gpio_host_proxy_device); ++ } ++ ++ deb_info("device class created correctly\n"); // Made it! device was initialized ++ ++ return 0; ++} ++ ++/* ++ * Removes module, sends appropriate message to kernel ++ */ ++static int gpio_host_proxy_remove(struct platform_device *pdev) ++{ ++ deb_info("removing module.\n"); ++ device_destroy(gpio_host_proxy_class, MKDEV(major_number, 0)); // remove the device ++ class_unregister(gpio_host_proxy_class); // unregister the device class ++ class_destroy(gpio_host_proxy_class); // remove the device class ++ unregister_chrdev(major_number, DEVICE_NAME); // unregister the major number ++ deb_info("Goodbye from the LKM!\n"); ++ unregister_chrdev(major_number, DEVICE_NAME); ++ return 0; ++} ++ ++/* ++ * Opens device module, sends appropriate message to kernel ++ */ ++static int open(struct inode *inodep, struct file *filep) ++{ ++ deb_info("device opened.\n"); ++ return 0; ++} ++ ++/* ++ * Closes device module, sends appropriate message to kernel ++ */ ++static int close(struct inode *inodep, struct file *filep) ++{ ++ deb_info("device closed.\n"); ++ return 0; ++} ++ ++/* ++ * Reads from device, displays in userspace, and deletes the read data ++ */ ++static ssize_t read(struct file *filep, char *buffer, size_t len, loff_t *offset) ++{ ++ deb_info("read stub\n"); ++ return 0; ++} ++ ++// TODO ++/* ++ * Checks if the value to transmit through the ++ * gpio-host is allowed by the device tree configuration ++ */ ++/* ++static bool check_if_allowed(int val) ++{ ++ return false; ++} ++*/ ++ ++/* ++ * Writes to the device ++ */ ++ ++static ssize_t write(struct file *filep, const char *buffer, size_t len, loff_t *offset) ++{ ++ unsigned int ret; ++ unsigned long int ret_l; ++ struct tegra_gpio_pt *kbuf = NULL; ++ tegra_gpio_pt_extended *kbuf_ext = NULL; ++ ++ static struct file *file; ++ static struct inode *inode = NULL; ++ struct gpio_chip *chip; ++ #ifdef GPIO_DEBUG ++ struct gpio_chip *chip_alt; ++ #endif ++ ++ char *return_buffer = (char *)buffer; ++ char *read_buffer = (char *)buffer; ++ ++ deb_info("writeing %zu bytes to chardev", len); ++ ++ /* removed because condition is covered by susequent checks on 'len' ++ if (len > 65535) { ++ pr_err("count %zu exceeds max # of bytes allowed, aborting write", len); ++ return -EINVAL; ++ } */ ++ ++ // We allow tegra_gpio_pt alone or with tegra_gpio_pt_extended (verify later) ++ if( len != sizeof(struct tegra_gpio_pt) && len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) { ++ pr_err("Illegal chardev data length. Expected %ld or %ld, got %ld", sizeof(struct tegra_gpio_pt), sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), len); ++ return -ENOEXEC; ++ } ++ ++ if(!offset) { ++ pr_err("offset pointer is null, ignoring offset\n"); ++ } ++ else { ++ read_buffer += (*offset); ++ } ++ ++ kbuf = kmalloc(len, GFP_KERNEL); ++ if ( !kbuf ) { ++ pr_err("kbuf memory allocation failed\n"); ++ return -ENOMEM; ++ } ++ memset(kbuf, 0, len); ++ ++ // Copy header ++ if (copy_from_user(kbuf, read_buffer, sizeof(struct tegra_gpio_pt))) { ++ pr_err("copy_from_user failed\n"); ++ kfree(kbuf); ++ return -ENOMEM; ++ } ++ deb_verbose("kbuf is set up, kbuf=%p", kbuf); ++ ++ if( len == (sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) ) { ++ kbuf_ext = (tegra_gpio_pt_extended *)(kbuf + 1); ++ deb_verbose("kbuf_ext is set up kbuf_ext=%p", kbuf_ext); ++ } ++ ++ // print copied user parameters ++ hexDump ("Chardev input", kbuf, len); ++ ++ ++ // make gpio-host type call to gpio ++ deb_verbose("enter switch with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); ++ ++ switch (kbuf->signal) { ++ case GPIO_REQ: ++ // if(kbuf->chipnum & 0xfe) { // 0 and 1 are allowed values & mask allows fastcheck, marginal save ++ if(kbuf->chipnum >= MAX_CHIP) { // direct copmparison is more future flexible ++ // te!, ++ pr_err("Illegal value for chip number\n"); ++ kfree(kbuf); ++ return -ENODEV; ++ } ++ chip = find_chip_by_id(kbuf->chipnum); ++ #ifdef GPIO_DEBUG_VERBOSE ++ chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); ++ if(chip != chip_alt) ++ deb_debug("conflicting chip pointers -- primary %p, alternative %p", chip, chip_alt); ++ #endif ++ if(!chip) { ++ pr_err("In GPIO_REQ, chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); ++ kfree(kbuf); ++ return -ENODEV; ++ } ++ ++ deb_verbose("GPIO_REQ, using GPIO chip %s, for device %d, pvalue = %p", chip->label, kbuf->chipnum, chip); ++ ret = chip->request(chip, kbuf->offset); ++ goto end; ++ break; ++ case GPIO_FREE: ++ chip = find_chip_by_id(kbuf->chipnum); ++ deb_verbose("GPIO_FREE\n"); ++ chip->free(chip, kbuf->offset); ++ // chip_alt = NULL; ++ goto end; ++ break; ++ case GPIO_GET_DIR: ++ chip = find_chip_by_id(kbuf->chipnum); ++ deb_verbose("GPIO_GET_DIR\n"); ++ ret = chip->get_direction(chip, kbuf->offset); ++ goto retval; ++ break; ++ case GPIO_SET_IN: ++ chip = find_chip_by_id(kbuf->chipnum); ++ deb_verbose("GPIO_SET_IN\n"); ++ ret = chip->direction_input(chip, kbuf->offset); ++ goto retval; ++ break; ++ case GPIO_SET_OUT: ++ chip = find_chip_by_id(kbuf->chipnum); ++ deb_verbose("GPIO_SET_OUT, chip pvalue 0 %p", chip); ++ ret = chip->direction_output(chip, kbuf->offset, kbuf->level); ++ goto retval; ++ break; ++ case GPIO_GET: ++ deb_verbose("GPIO_GET\n"); ++ chip = find_chip_by_id(kbuf->chipnum); ++ ret = chip->get(chip, kbuf->offset); ++ goto retval; ++ break; ++ case GPIO_SET: ++ chip = find_chip_by_id(kbuf->chipnum); ++ deb_verbose("GPIO_SET, set %d at offset 0x%x in gpiochip %s", kbuf->level, kbuf->offset, chip->label); ++ chip->set(chip, kbuf->offset, kbuf->level); ++ goto end; ++ break; ++ case GPIO_CONFIG: ++ chip = find_chip_by_id(kbuf->chipnum); ++ deb_verbose("GPIO_CONFIG\n"); ++ chip->set_config(chip, kbuf->offset, kbuf_ext->config); // arg mapped to unsigned long config ++ goto end; ++ break; ++ case GPIO_TIMESTAMP_CTRL: ++ chip = find_chip_by_id(kbuf->chipnum); ++ deb_verbose("GPIO_TIMESTAMP_CTRL\n"); ++ ret = chip->timestamp_control(chip, kbuf->offset, kbuf->level); // mapping level onto enable ++ goto retval; ++ break; ++ case GPIO_TIMESTAMP_READ: ++ chip = find_chip_by_id(kbuf->chipnum); ++ deb_verbose("GPIO_TIMESTAMP_READ\n"); ++ ret = chip->timestamp_read(chip, kbuf->offset, (u64 *)return_buffer); // timestamp is u64, return value as pointer ++ if(ret) { ++ pr_err("GPIO_TIMESTAMP_READ error\n"); ++ goto end; ++ } ++ // timestamp_read returns value directly to return_buffer ++ goto end; ++ break; ++ case GPIO_SUSPEND_CONF: ++ chip = find_chip_by_id(kbuf->chipnum); ++ deb_verbose("GPIO_SUSPEND_CONF\n"); ++ if(!kbuf_ext) { ++ pr_err("Parameter error in GPIO_SUSPEND_CONF\n"); ++ return -EINVAL; ++ } ++ ret = chip->suspend_configure(chip, kbuf->offset, kbuf_ext->dflags); ++ goto retval; ++ break; ++ case GPIO_ADD_PINRANGES: ++ chip = find_chip_by_id(kbuf->chipnum); ++ deb_verbose("GPIO_ADD_PINRANGES\n"); ++ ret = chip->add_pin_ranges(chip); ++ goto retval; ++ break; ++ }; ++ ++ /* ioctl signals use flieops because it relises on the standard gpio chardevs ++ static const struct file_operations gpio_fileops = { ++ .release = gpio_chrdev_release, ++ .open = gpio_chrdev_open, ++ .poll = lineinfo_watch_poll, ++ .read = lineinfo_watch_read, ++ .owner = THIS_MODULE, ++ .llseek = no_llseek, ++ .unlocked_ioctl = gpio_ioctl, ++ #ifdef CONFIG_COMPAT ++ .compat_ioctl = gpio_ioctl_compat, ++ #endif ++ }; ++ */ ++ ++ if(kbuf_ext) { ++ switch (kbuf->signal) { ++ /* commands to ioctl below (the std gpio chardev) ++ * not fully implemented ++ * linehandle_create -- when userspace requests output (called by gpio_ioctl) -- bypasses the chardev ++ * linehandle_ioctl -- linehandle_ioctl when userspace does actual io (toggles pin) ++ * cmd: ++ * GPIOHANDLE_GET_LINE_VALUES_IOCTL, ++ * GPIOHANDLE_SET_LINE_VALUES_IOCTL, ++ * GPIOHANDLE_SET_CONFIG_IOCTL ++ * arg: user input or output ++ */ ++ ++ // We could want to use the stock gpio chardev (/dev/gpiochip0 and /dev/gpiochip1) /bc userspace functions use it ++ // this code is not yet complete and it mey be better to use the stock devices directly. ++ case GPIO_CHARDEV_OPEN: // .open = gpio_chrdev_open ++ file = filp_open(tegra_chiplabel[kbuf->chipnum], O_RDWR, 0); ++ if (IS_ERR(file)) { ++ pr_err("GPIO, failed to open chardev for chip %s: %ld", tegra_chiplabel[kbuf->chipnum], PTR_ERR(file)); ++ kfree(kbuf); ++ return -ENOENT; ++ } ++ // note: inode and file are static variables ++ inode = file->f_path.dentry->d_inode; ++ // defined as: static int gpio_chrdev_open(struct inode *inode, struct file *file) ++ ret = file->f_op->open(inode, file); ++ goto retval; ++ break; ++ case GPIO_CHARDEV_IOCTL: // .unlocked_ioctl = gpio_ioctl ++ // user space triggers gpio_ioctl -- it is .unlocked_ioctl on the chardev ++ if( !file ) { ++ pr_err("GPIO, chardev file was expected to be open\n"); ++ kfree(kbuf); ++ return -ENOENT; ++ } ++ // defined as: static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++ ret_l = file->f_op->unlocked_ioctl(file, kbuf->cmd, kbuf_ext->arg); // arg is pointer data which should have been copied from userspace ++ goto retlong; ++ break; ++ case GPIO_CHARDEV_RELEASE: // .release = gpio_chrdev_release ++ if( !file ) { ++ pr_err("GPIO, chardev file was expected to be open\n"); ++ kfree(kbuf); ++ return -ENOENT; ++ } ++ // defined as: static int gpio_chrdev_release(struct inode *inode, struct file *file) ++ ret = file->f_op->release(inode, file); ++ goto retval; ++ break; ++ case GPIO_CHARDEV_POLL: // .poll = lineinfo_watch_poll ++ if( !file ) { ++ pr_err("GPIO, chardev file was expected to be open\n"); ++ kfree(kbuf); ++ return -ENOENT; ++ } ++ // defined as: static __poll_t lineinfo_watch_poll(struct file *file, struct poll_table_struct *pollt) ++ ret = file->f_op->poll(file, kbuf_ext->poll); // TODO arg is pointer data which should have been copied ++ goto retval; // __poll_t is of size unsigned int ++ break; ++ case GPIO_CHARDEV_READ: // .read = lineinfo_watch_read ++ if( !file ) { ++ pr_err("GPIO, chardev file was expected to be open\n"); ++ kfree(kbuf); ++ return -ENOENT; ++ } ++ // defined as: static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, size_t count, loff_t *off) ++ ret = file->f_op->read(file, return_buffer, kbuf_ext->count, NULL); // ++ if (ret) { ++ pr_err("Reading lineinfo returned zero\n"); ++ kfree(kbuf); ++ return -EFAULT; ++ } ++ return -ENXIO; ++ case GPIO_CHARDEV_OWNER: // .owner = THIS_MODULE ++ if (copy_to_user(return_buffer, file->f_op->owner->name, strlen(file->f_op->owner->name)+1)) { ++ pr_err("GPIO, copying user return value failed\n"); ++ kfree(kbuf); ++ return -EFAULT; ++ } ++ // ret_sz = strlen(file->f_op->owner->name) + 1; ++ // goto generic_ret ++ break; ++ default: ++ pr_err("GPIO, Illegal proxy signal type\n"); ++ kfree(kbuf); ++ return -EPERM; ++ break; ++ }; ++ }; ++ ++ goto end; ++ ++ retlong: ++ if ( copy_to_user(return_buffer, &ret_l, sizeof(ret_l)) ) { ++ pr_err("GPIO, copying int user return value failed\n"); ++ kfree(kbuf); ++ return -EFAULT; ++ }; ++ ++ goto end; ++ ++ retval: ++ if ( copy_to_user(return_buffer, &ret, sizeof(ret)) ) { ++ pr_err("GPIO, copying long int user return value failed\n"); ++ kfree(kbuf); ++ return -EFAULT; ++ }; ++ ++ goto end; ++ ++ end: ++ kfree(kbuf); ++ return len; ++} ++ ++/* module creation -- see also gpio_host_proxy_probe and gpio_host_proxy_remove */ ++ ++static const struct of_device_id gpio_host_proxy_ids[] = { ++ { .compatible = "nvidia,gpio-host-proxy" }, ++ { } ++}; ++ ++static struct platform_driver gpio_host_proxy_driver = { ++ .driver = { ++ .name = "gpio_host_proxy", ++ .owner = THIS_MODULE, ++ .of_match_table = gpio_host_proxy_ids, ++ }, ++ .probe = gpio_host_proxy_probe, ++ .remove = gpio_host_proxy_remove, ++}; ++// builtin_platform_driver(gpio_host_proxy_driver); ++ ++static int __init gpio_host_proxy_init(void) ++{ ++ int ret = 0; ++ ++ ret = platform_driver_register(&gpio_host_proxy_driver); ++ if (ret != 0) { ++ pr_err("GPIO, Error %d registering gpio host proxy driver", ret); ++ } else { ++ deb_info("GPIO gpio host proxy driver registered successfully\n"); ++ } ++ ++ return ret; ++} ++ ++static void __exit gpio_host_proxy_exit(void) ++{ ++ platform_driver_unregister(&gpio_host_proxy_driver); ++ deb_info("GPIO gpio host proxy driver unregistered\n"); ++} ++ ++module_init(gpio_host_proxy_init); ++module_exit(gpio_host_proxy_exit); +diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.h b/drivers/gpio-host-proxy/gpio-host-proxy.h +new file mode 100644 +index 0000000..b714531 +--- /dev/null ++++ b/drivers/gpio-host-proxy/gpio-host-proxy.h +@@ -0,0 +1,79 @@ ++#ifndef __GPIO_HOST_PROXY__H__ ++#define __GPIO_HOST_PROXY__H__ ++ ++#include // For __iomem definition ++#include // For functions related to I/O operations ++#include ++#include // for gpiod_flags ++ ++// as a workaround this struct is copied here from drivers/gpio/gpiolib.h ++// including the original header file does not work becaue proxy drivers are overlay and absolute paths will change ++// this copied struct is incomplete and subset of the "real" one ++struct gpio_device { ++ int id; ++ struct device dev; ++ // a huge number of members are removed here -- do not sizeof this struct !!! ++}; ++ ++ ++/* values to be used as "signal" values in struct tegra_gpio_pt */ ++#define GPIO_CHARDEV_OPEN '1' // .open = gpio_chrdev_open ++#define GPIO_CHARDEV_IOCTL '2' // .unlocked_ioctl = gpio_ioctl -- handles IO operation, get linehandle, set direction ++#define GPIO_CHARDEV_POLL '3' // .poll = lineinfo_watch_poll ++#define GPIO_CHARDEV_READ '4' // .read = lineinfo_watch_read ++#define GPIO_CHARDEV_OWNER '5' // .owner = THIS_MODULE ++#define GPIO_CHARDEV_SEEK '6' // .llseek = no_llseek ++#define GPIO_CHARDEV_RELEASE '7' // .release = gpio_chrdev_release ++ ++#define GPIO_SET 's' // set level ++#define GPIO_GET 'g' // get level ++#define GPIO_GET_DIR 'd' // get direction ++#define GPIO_SET_IN 'i' // set direction to input ++#define GPIO_SET_OUT 'o' // set direction to output ++#define GPIO_CONFIG 'c' // set config ++#define GPIO_SET_BY_NAME 'n' // set config ++ ++#define GPIO_REQ 'r' // generic request ++#define GPIO_FREE 'f' // free ++ ++#define GPIO_TIMESTAMP_CTRL 'C' // timestamp control ++#define GPIO_TIMESTAMP_READ 'R' // timestamp read ++#define GPIO_SUSPEND_CONF 'S' // suspend configure ++#define GPIO_ADD_PINRANGES 'P' // add_pin_ranges ++ ++// helpers to identify chip ++#define TEGRA_GPIO_CHIP 0 // tegra-gpio gpio_main_chip ++#define TEGRA_GPIO_AON_CHIP 1 // tegra-gpio-aon gpio_aon_chip ++#define TEGRA_GPIO_LABEL "tegra234-gpio\x00\x00\x00\x00\x00\x00\x00" // gpio_main_chip / gpiochip0 --padded to 20 bytes ++#define TEGRA_GPIO_AON_LABEL "tegra234-gpio-aon\x00\x00\x00" // gpio_aon_chip / gpiochip1 --padded to 20 bytes ++#define LABEL_SIZE 20 ++ ++// struct __attribute__((packed)) tegra_gpio_pt { ++struct tegra_gpio_pt { ++ unsigned char signal; // defines operation ++ unsigned char chipnum; // number of gpio chip (gpiochip0 or gpiochip1) ++ unsigned char level; // padding to reach 8 byte word alignment ++ unsigned char offset; // address offset for gpio pin ++ u32 cmd; // gpio_ioctl command ++ // tegra_gpio_pt_extended p2; // extended parameters -- in second word of struct ++}; ++ ++typedef union extended { ++ // int level; // pin level to be set ++ unsigned long config; // pin configuration ++ int enable; ++ size_t count; // lineinfo read size ++ struct poll_table_struct *poll; ++ enum gpiod_flags dflags ; ++ u64 arg; // gpio_ioctl argument (this is interpreted as a pointer) ++} tegra_gpio_pt_extended; ++ ++#define MAX_CHIP 2 ++ ++_Static_assert( sizeof(struct tegra_gpio_pt) == 8, ++ "tegra_gpio_pt size is not 8 bytes." ); ++ ++_Static_assert( sizeof(tegra_gpio_pt_extended) == 8, ++ "tegra_gpio_pt_extended size is not 8 bytes." ); ++ ++#endif diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0005-gpio-overlay.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0005-gpio-overlay.patch new file mode 100644 index 000000000..f4999c718 --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0005-gpio-overlay.patch @@ -0,0 +1,18 @@ +diff --git a/kernel-int-overlays.txt b/kernel-int-overlays.txt +index 6a59a925233b..c1d183bc1a92 100644 +--- a/kernel-int-overlays.txt ++++ b/kernel-int-overlays.txt +@@ -3,3 +3,4 @@ nvlink + nvgpu + nvgpu-next + nvidia-t239 ++gpio-virt +diff --git a/kernel-overlays.txt b/kernel-overlays.txt +index 1a8cf218b2a9..63f3d6be9766 100644 +--- a/kernel-overlays.txt ++++ b/kernel-overlays.txt +@@ -1,3 +1,4 @@ + nvidia + nvlink + nvgpu ++gpio-virt diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0006-defconfig-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0006-defconfig-kernel.patch new file mode 100644 index 000000000..cc906b8b0 --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0006-defconfig-kernel.patch @@ -0,0 +1,8481 @@ +diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig +index bda2dff571c4..73a92ece4ca0 100644 +--- a/arch/arm64/configs/defconfig ++++ b/arch/arm64/configs/defconfig +@@ -1,201 +1,1283 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm64 5.10.104 Kernel Configuration ++# ++CONFIG_CC_VERSION_TEXT="gcc (GCC) 9.5.0" ++CONFIG_CC_IS_GCC=y ++CONFIG_GCC_VERSION=90500 ++CONFIG_LD_VERSION=240000000 ++CONFIG_CLANG_VERSION=0 ++CONFIG_LLD_VERSION=0 ++CONFIG_CC_CAN_LINK=y ++CONFIG_CC_HAS_ASM_GOTO=y ++CONFIG_CC_HAS_ASM_INLINE=y ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_TABLE_SORT=y ++CONFIG_THREAD_INFO_IN_TASK=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" + # CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_BUILD_SALT="" ++CONFIG_DEFAULT_INIT="" ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y + CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y + CONFIG_POSIX_MQUEUE=y ++CONFIG_POSIX_MQUEUE_SYSCTL=y + CONFIG_WATCH_QUEUE=y ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_USELIB is not set + CONFIG_AUDIT=y ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++CONFIG_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_GENERIC_IRQ_SHOW_LEVEL=y ++CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y ++CONFIG_GENERIC_IRQ_MIGRATION=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_IRQ_SIM=y ++CONFIG_IRQ_DOMAIN_HIERARCHY=y ++CONFIG_GENERIC_IRQ_IPI=y ++CONFIG_GENERIC_MSI_IRQ=y ++CONFIG_GENERIC_MSI_IRQ_DOMAIN=y ++CONFIG_IRQ_MSI_IOMMU=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++# CONFIG_GENERIC_IRQ_DEBUGFS is not set ++# end of IRQ subsystem ++ ++CONFIG_GENERIC_IRQ_MULTI_HANDLER=y ++CONFIG_GENERIC_TIME_VSYSCALL=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++# CONFIG_NO_HZ_FULL is not set + CONFIG_NO_HZ=y + CONFIG_HIGH_RES_TIMERS=y ++# end of Timers subsystem ++ ++# CONFIG_PREEMPT_NONE is not set ++# CONFIG_PREEMPT_VOLUNTARY is not set + CONFIG_PREEMPT=y ++CONFIG_PREEMPT_COUNT=y ++CONFIG_PREEMPTION=y ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set + CONFIG_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_SCHED_AVG_IRQ=y ++CONFIG_SCHED_THERMAL_PRESSURE=y + CONFIG_BSD_PROCESS_ACCT=y + CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_TASKSTATS=y ++CONFIG_TASK_DELAY_ACCT=y + CONFIG_TASK_XACCT=y + CONFIG_TASK_IO_ACCOUNTING=y ++# CONFIG_PSI is not set ++# end of CPU/Task time and stats accounting ++ ++CONFIG_CPU_ISOLATION=y ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++CONFIG_PREEMPT_RCU=y ++# CONFIG_RCU_EXPERT is not set ++CONFIG_SRCU=y ++CONFIG_TREE_SRCU=y ++CONFIG_TASKS_RCU_GENERIC=y ++CONFIG_TASKS_RCU=y ++CONFIG_TASKS_RUDE_RCU=y ++CONFIG_TASKS_TRACE_RCU=y ++CONFIG_RCU_STALL_COMMON=y ++CONFIG_RCU_NEED_SEGCBLIST=y ++# end of RCU Subsystem ++ + CONFIG_IKCONFIG=y + CONFIG_IKCONFIG_PROC=y ++# CONFIG_IKHEADERS is not set ++CONFIG_LOG_BUF_SHIFT=20 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=14 ++CONFIG_GENERIC_SCHED_CLOCK=y ++ ++# ++# Scheduler features ++# ++# CONFIG_UCLAMP_TASK is not set ++# end of Scheduler features ++ ++CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y ++CONFIG_CC_HAS_INT128=y ++CONFIG_ARCH_SUPPORTS_INT128=y + CONFIG_CGROUPS=y ++CONFIG_PAGE_COUNTER=y + CONFIG_MEMCG=y ++CONFIG_MEMCG_SWAP=y ++CONFIG_MEMCG_KMEM=y + CONFIG_BLK_CGROUP=y ++CONFIG_CGROUP_WRITEBACK=y + CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y + CONFIG_CFS_BANDWIDTH=y + CONFIG_RT_GROUP_SCHED=y + CONFIG_CGROUP_PIDS=y ++# CONFIG_CGROUP_RDMA is not set + CONFIG_CGROUP_FREEZER=y + CONFIG_CGROUP_HUGETLB=y + CONFIG_CPUSETS=y ++CONFIG_PROC_PID_CPUSET=y + CONFIG_CGROUP_DEVICE=y + CONFIG_CGROUP_CPUACCT=y + CONFIG_CGROUP_PERF=y ++# CONFIG_CGROUP_BPF is not set ++# CONFIG_CGROUP_DEBUG is not set ++CONFIG_SOCK_CGROUP_DATA=y + CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_TIME_NS=y ++CONFIG_IPC_NS=y + CONFIG_USER_NS=y ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set + CONFIG_RELAY=y + CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++CONFIG_RD_ZSTD=y ++# CONFIG_BOOT_CONFIG is not set ++CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_LD_ORPHAN_WARN=y ++CONFIG_SYSCTL=y ++CONFIG_HAVE_UID16=y ++CONFIG_SYSCTL_EXCEPTION_TRACE=y ++CONFIG_BPF=y ++CONFIG_EXPERT=y ++CONFIG_UID16=y ++CONFIG_MULTIUSER=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++CONFIG_FHANDLE=y ++CONFIG_POSIX_TIMERS=y ++CONFIG_PRINTK=y ++CONFIG_PRINTK_NMI=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_FUTEX_PI=y ++CONFIG_HAVE_FUTEX_CMPXCHG=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_IO_URING=y ++CONFIG_ADVISE_SYSCALLS=y ++CONFIG_MEMBARRIER=y ++CONFIG_KALLSYMS=y + CONFIG_KALLSYMS_ALL=y ++CONFIG_KALLSYMS_BASE_RELATIVE=y ++CONFIG_BPF_SYSCALL=y ++CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y ++# CONFIG_BPF_JIT_ALWAYS_ON is not set ++CONFIG_BPF_JIT_DEFAULT_ON=y ++# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set ++# CONFIG_BPF_PRELOAD is not set ++# CONFIG_USERFAULTFD is not set ++CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y ++CONFIG_KCMP=y ++CONFIG_RSEQ=y ++# CONFIG_DEBUG_RSEQ is not set + CONFIG_EMBEDDED=y ++CONFIG_HAVE_PERF_EVENTS=y ++# CONFIG_PC104 is not set ++ ++# ++# Kernel Performance Events And Counters ++# ++CONFIG_PERF_EVENTS=y ++# CONFIG_DEBUG_PERF_USE_VMALLOC is not set ++# end of Kernel Performance Events And Counters ++ ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++# CONFIG_SLUB_MEMCG_SYSFS_ON is not set + # CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++CONFIG_SLAB_MERGE_DEFAULT=y + CONFIG_SLAB_FREELIST_RANDOM=y + CONFIG_SLAB_FREELIST_HARDENED=y + CONFIG_SHUFFLE_PAGE_ALLOCATOR=y ++CONFIG_SLUB_CPU_PARTIAL=y ++CONFIG_SYSTEM_DATA_VERIFICATION=y + CONFIG_PROFILING=y ++CONFIG_TRACEPOINTS=y ++# end of General setup ++ ++CONFIG_ARM64=y ++CONFIG_64BIT=y ++CONFIG_MMU=y ++CONFIG_ARM64_PAGE_SHIFT=12 ++CONFIG_ARM64_CONT_PTE_SHIFT=4 ++CONFIG_ARM64_CONT_PMD_SHIFT=4 ++CONFIG_ARCH_MMAP_RND_BITS_MIN=18 ++CONFIG_ARCH_MMAP_RND_BITS_MAX=33 ++CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 ++CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16 ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CSUM=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_ZONE_DMA=y ++CONFIG_ZONE_DMA32=y ++CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y ++CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y ++CONFIG_SMP=y ++CONFIG_KERNEL_MODE_NEON=y ++CONFIG_FIX_EARLYCON_MEM=y ++CONFIG_PGTABLE_LEVELS=4 ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_ARCH_PROC_KCORE_TEXT=y ++ ++# ++# Platform selection ++# ++# CONFIG_ARCH_ACTIONS is not set ++# CONFIG_ARCH_AGILEX is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_ALPINE is not set ++# CONFIG_ARCH_BCM2835 is not set ++# CONFIG_ARCH_BCM_IPROC is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_BITMAIN is not set ++# CONFIG_ARCH_BRCMSTB is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SPARX5 is not set ++# CONFIG_ARCH_K3 is not set ++# CONFIG_ARCH_LAYERSCAPE is not set ++# CONFIG_ARCH_LG1K is not set ++# CONFIG_ARCH_HISI is not set ++# CONFIG_ARCH_KEEMBAY is not set ++# CONFIG_ARCH_MEDIATEK is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_REALTEK is not set ++# CONFIG_ARCH_RENESAS is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_S32 is not set ++# CONFIG_ARCH_SEATTLE is not set ++# CONFIG_ARCH_STRATIX10 is not set ++# CONFIG_ARCH_SYNQUACER is not set + CONFIG_ARCH_TEGRA=y ++# CONFIG_ARCH_SPRD is not set ++# CONFIG_ARCH_THUNDER is not set ++# CONFIG_ARCH_THUNDER2 is not set ++# CONFIG_ARCH_UNIPHIER is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_VISCONTI is not set ++# CONFIG_ARCH_XGENE is not set ++# CONFIG_ARCH_ZX is not set ++# CONFIG_ARCH_ZYNQMP is not set ++# end of Platform selection ++ ++# ++# Kernel Features ++# ++ ++# ++# ARM errata workarounds via the alternatives framework ++# ++CONFIG_ARM64_WORKAROUND_CLEAN_CACHE=y ++CONFIG_ARM64_ERRATUM_826319=y ++CONFIG_ARM64_ERRATUM_827319=y ++CONFIG_ARM64_ERRATUM_824069=y ++CONFIG_ARM64_ERRATUM_819472=y ++CONFIG_ARM64_ERRATUM_832075=y ++CONFIG_ARM64_ERRATUM_834220=y ++CONFIG_ARM64_ERRATUM_845719=y ++CONFIG_ARM64_ERRATUM_843419=y ++CONFIG_ARM64_ERRATUM_1024718=y ++CONFIG_ARM64_ERRATUM_1418040=y ++CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT=y ++CONFIG_ARM64_ERRATUM_1165522=y ++CONFIG_ARM64_ERRATUM_1319367=y ++CONFIG_ARM64_ERRATUM_1530923=y ++CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y ++CONFIG_ARM64_ERRATUM_1286807=y ++CONFIG_ARM64_ERRATUM_1463225=y ++CONFIG_ARM64_ERRATUM_1542419=y ++CONFIG_ARM64_ERRATUM_1508412=y ++CONFIG_CAVIUM_ERRATUM_22375=y ++CONFIG_CAVIUM_ERRATUM_23154=y ++CONFIG_CAVIUM_ERRATUM_27456=y ++CONFIG_CAVIUM_ERRATUM_30115=y ++CONFIG_CAVIUM_TX2_ERRATUM_219=y ++CONFIG_FUJITSU_ERRATUM_010001=y ++CONFIG_HISILICON_ERRATUM_161600802=y ++CONFIG_QCOM_FALKOR_ERRATUM_1003=y ++CONFIG_QCOM_FALKOR_ERRATUM_1009=y ++CONFIG_QCOM_QDF2400_ERRATUM_0065=y ++CONFIG_QCOM_FALKOR_ERRATUM_E1041=y ++CONFIG_NVIDIA_CARMEL_CNP_ERRATUM=y ++CONFIG_SOCIONEXT_SYNQUACER_PREITS=y ++# end of ARM errata workarounds via the alternatives framework ++ ++CONFIG_ARM64_4K_PAGES=y ++# CONFIG_ARM64_16K_PAGES is not set ++# CONFIG_ARM64_64K_PAGES is not set ++# CONFIG_ARM64_VA_BITS_39 is not set + CONFIG_ARM64_VA_BITS_48=y ++CONFIG_ARM64_VA_BITS=48 ++CONFIG_ARM64_PA_BITS_48=y ++CONFIG_ARM64_PA_BITS=48 ++# CONFIG_CPU_BIG_ENDIAN is not set ++CONFIG_CPU_LITTLE_ENDIAN=y + CONFIG_SCHED_MC=y + CONFIG_SCHED_SMT=y ++CONFIG_NR_CPUS=256 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_NUMA is not set ++CONFIG_HOLES_IN_ZONE=y ++# CONFIG_HZ_100 is not set ++CONFIG_HZ_250=y ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=250 ++CONFIG_SCHED_HRTICK=y ++CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y ++CONFIG_ARCH_SPARSEMEM_ENABLE=y ++CONFIG_ARCH_SPARSEMEM_DEFAULT=y ++CONFIG_ARCH_SELECT_MEMORY_MODEL=y ++CONFIG_ARCH_FLATMEM_ENABLE=y ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HW_PERF_EVENTS=y ++CONFIG_SYS_SUPPORTS_HUGETLBFS=y ++CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y ++CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y ++CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y + CONFIG_PARAVIRT=y ++# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set + CONFIG_KEXEC=y + CONFIG_KEXEC_FILE=y ++# CONFIG_KEXEC_SIG is not set + CONFIG_CRASH_DUMP=y ++# CONFIG_XEN is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_UNMAP_KERNEL_AT_EL0=y + # CONFIG_RODATA_FULL_DEFAULT_ENABLED is not set + CONFIG_ARM64_SW_TTBR0_PAN=y ++CONFIG_ARM64_TAGGED_ADDR_ABI=y + CONFIG_COMPAT=y ++CONFIG_KUSER_HELPERS=y + CONFIG_ARMV8_DEPRECATED=y + CONFIG_SWP_EMULATION=y + CONFIG_CP15_BARRIER_EMULATION=y + CONFIG_SETEND_EMULATION=y ++ ++# ++# ARMv8.1 architectural features ++# + # CONFIG_ARM64_HW_AFDBM is not set ++CONFIG_ARM64_PAN=y ++CONFIG_AS_HAS_LSE_ATOMICS=y ++CONFIG_ARM64_LSE_ATOMICS=y ++CONFIG_ARM64_USE_LSE_ATOMICS=y ++CONFIG_ARM64_VHE=y ++# end of ARMv8.1 architectural features ++ ++# ++# ARMv8.2 architectural features ++# ++CONFIG_ARM64_UAO=y ++# CONFIG_ARM64_PMEM is not set ++CONFIG_ARM64_RAS_EXTN=y ++CONFIG_ARM64_CNP=y ++# end of ARMv8.2 architectural features ++ ++# ++# ARMv8.3 architectural features ++# ++CONFIG_CC_HAS_BRANCH_PROT_PAC_RET=y ++CONFIG_CC_HAS_SIGN_RETURN_ADDRESS=y ++CONFIG_AS_HAS_PAC=y ++CONFIG_AS_HAS_CFI_NEGATE_RA_STATE=y ++# end of ARMv8.3 architectural features ++ ++# ++# ARMv8.4 architectural features ++# ++CONFIG_ARM64_AMU_EXTN=y ++# end of ARMv8.4 architectural features ++ ++# ++# ARMv8.5 architectural features ++# ++CONFIG_ARM64_BTI=y ++CONFIG_CC_HAS_BRANCH_PROT_PAC_RET_BTI=y ++CONFIG_ARM64_E0PD=y ++CONFIG_ARCH_RANDOM=y ++CONFIG_ARM64_AS_HAS_MTE=y ++CONFIG_ARM64_MTE=y ++# end of ARMv8.5 architectural features ++ ++CONFIG_ARM64_SVE=y ++CONFIG_ARM64_MODULE_PLTS=y ++# CONFIG_ARM64_PSEUDO_NMI is not set ++CONFIG_RELOCATABLE=y + CONFIG_RANDOMIZE_BASE=y ++CONFIG_RANDOMIZE_MODULE_REGION_FULL=y ++CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y ++CONFIG_STACKPROTECTOR_PER_TASK=y ++# CONFIG_TEGRA_EBP is not set ++CONFIG_TEGRA_PSC=y ++# end of Kernel Features ++ ++# ++# Boot options ++# ++# CONFIG_ARM64_ACPI_PARKING_PROTOCOL is not set ++CONFIG_CMDLINE="" ++CONFIG_EFI_STUB=y ++CONFIG_EFI=y ++CONFIG_DMI=y ++# end of Boot options ++ ++CONFIG_SYSVIPC_COMPAT=y ++CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y ++CONFIG_ARCH_ENABLE_THP_MIGRATION=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++# CONFIG_SUSPEND_SKIP_SYNC is not set ++# CONFIG_HIBERNATION is not set ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y + CONFIG_PM_AUTOSLEEP=y + CONFIG_PM_WAKELOCKS=y ++CONFIG_PM_WAKELOCKS_LIMIT=100 ++CONFIG_PM_WAKELOCKS_GC=y ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++CONFIG_PM_CLK=y ++CONFIG_PM_GENERIC_DOMAINS=y + CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y ++CONFIG_PM_GENERIC_DOMAINS_SLEEP=y ++CONFIG_PM_GENERIC_DOMAINS_OF=y ++CONFIG_CPU_PM=y + CONFIG_ENERGY_MODEL=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++# end of Power management options ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Idle ++# ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y ++# CONFIG_CPU_IDLE_GOV_LADDER is not set ++CONFIG_CPU_IDLE_GOV_MENU=y ++# CONFIG_CPU_IDLE_GOV_TEO is not set ++CONFIG_DT_IDLE_STATES=y ++ ++# ++# ARM CPU Idle Drivers ++# + CONFIG_ARM_CPUIDLE=y + CONFIG_ARM_PSCI_CPUIDLE=y ++CONFIG_ARM_PSCI_CPUIDLE_DOMAIN=y ++CONFIG_CPU_IDLE_TEGRA19X=y + CONFIG_CPU_IDLE_TEGRA_AUTO=y ++# end of ARM CPU Idle Drivers ++# end of CPU Idle ++ ++# ++# CPU Frequency scaling ++# + CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_ATTR_SET=y ++CONFIG_CPU_FREQ_GOV_COMMON=y + CONFIG_CPU_FREQ_STAT=y + CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y + CONFIG_CPU_FREQ_GOV_POWERSAVE=y + CONFIG_CPU_FREQ_GOV_USERSPACE=y + CONFIG_CPU_FREQ_GOV_ONDEMAND=y + CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y + CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y ++ ++# ++# CPU frequency scaling drivers ++# + CONFIG_CPUFREQ_DT=y ++CONFIG_CPUFREQ_DT_PLATDEV=y + CONFIG_ACPI_CPPC_CPUFREQ=m ++CONFIG_ARM_TEGRA20_CPUFREQ=y ++CONFIG_ARM_TEGRA124_CPUFREQ=y + CONFIG_ARM_TEGRA186_CPUFREQ=y ++CONFIG_ARM_TEGRA194_CPUFREQ=y ++# end of CPU Frequency scaling ++# end of CPU Power Management ++ ++# ++# Firmware Drivers ++# ++# CONFIG_ARM_SCMI_PROTOCOL is not set + CONFIG_ARM_SCPI_PROTOCOL=m ++CONFIG_ARM_SCPI_POWER_DOMAIN=m ++# CONFIG_ARM_SDE_INTERFACE is not set ++# CONFIG_FIRMWARE_MEMMAP is not set ++CONFIG_DMIID=y + CONFIG_DMI_SYSFS=y ++# CONFIG_ISCSI_IBFT is not set ++# CONFIG_FW_CFG_SYSFS is not set ++# CONFIG_GOOGLE_FIRMWARE is not set ++ ++# ++# EFI (Extensible Firmware Interface) Support ++# ++CONFIG_EFI_ESRT=y + # CONFIG_EFI_VARS_PSTORE is not set ++CONFIG_EFI_PARAMS_FROM_FDT=y ++CONFIG_EFI_RUNTIME_WRAPPERS=y ++CONFIG_EFI_GENERIC_STUB=y ++CONFIG_EFI_ARMSTUB_DTB_LOADER=y ++CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y ++# CONFIG_EFI_BOOTLOADER_CONTROL is not set + CONFIG_EFI_CAPSULE_LOADER=y + CONFIG_EFI_TEST=m +-CONFIG_FB_EFI=y ++# CONFIG_RESET_ATTACK_MITIGATION is not set ++# CONFIG_EFI_DISABLE_PCI_DMA is not set ++# end of EFI (Extensible Firmware Interface) Support ++ ++CONFIG_UEFI_CPER=y ++CONFIG_UEFI_CPER_ARM=y ++CONFIG_EFI_EARLYCON=y ++CONFIG_EFI_CUSTOM_SSDT_OVERLAYS=y ++CONFIG_ARM_PSCI_FW=y ++# CONFIG_ARM_PSCI_CHECKER is not set ++CONFIG_HAVE_ARM_SMCCC=y ++CONFIG_HAVE_ARM_SMCCC_DISCOVERY=y ++CONFIG_ARM_SMCCC_SOC_ID=y ++ ++# ++# Tegra firmware driver ++# ++CONFIG_TEGRA_IVC=y ++CONFIG_TEGRA_BPMP=y ++# end of Tegra firmware driver ++ ++# ++# Tegra BPMP Driver ++# ++# end of Tegra BPMP Driver ++ ++# ++# Tegra firmware driver ++# ++# end of Tegra firmware driver ++ ++# ++# Tegra BPMP Driver ++# ++# end of Tegra BPMP Driver ++# end of Firmware Drivers ++ ++CONFIG_ARCH_SUPPORTS_ACPI=y + CONFIG_ACPI=y ++CONFIG_ACPI_GENERIC_GSI=y ++CONFIG_ACPI_CCA_REQUIRED=y ++# CONFIG_ACPI_DEBUGGER is not set ++CONFIG_ACPI_SPCR_TABLE=y ++# CONFIG_ACPI_EC_DEBUGFS is not set ++CONFIG_ACPI_AC=y ++CONFIG_ACPI_BATTERY=y ++CONFIG_ACPI_BUTTON=y ++CONFIG_ACPI_FAN=y ++# CONFIG_ACPI_TAD is not set ++# CONFIG_ACPI_DOCK is not set ++CONFIG_ACPI_PROCESSOR_IDLE=y ++CONFIG_ACPI_MCFG=y ++CONFIG_ACPI_CPPC_LIB=y ++CONFIG_ACPI_PROCESSOR=y ++CONFIG_ACPI_HOTPLUG_CPU=y ++CONFIG_ACPI_THERMAL=y ++CONFIG_ARCH_HAS_ACPI_TABLE_UPGRADE=y ++CONFIG_ACPI_TABLE_UPGRADE=y ++# CONFIG_ACPI_DEBUG is not set ++# CONFIG_ACPI_PCI_SLOT is not set ++CONFIG_ACPI_CONTAINER=y ++CONFIG_ACPI_HED=y ++# CONFIG_ACPI_CUSTOM_METHOD is not set ++# CONFIG_ACPI_BGRT is not set ++CONFIG_ACPI_REDUCED_HARDWARE_ONLY=y ++CONFIG_HAVE_ACPI_APEI=y + CONFIG_ACPI_APEI=y + CONFIG_ACPI_APEI_GHES=y ++# CONFIG_ACPI_APEI_PCIEAER is not set ++CONFIG_ACPI_APEI_SEA=y + CONFIG_ACPI_APEI_MEMORY_FAILURE=y + CONFIG_ACPI_APEI_EINJ=y ++# CONFIG_ACPI_APEI_ERST_DEBUG is not set ++# CONFIG_ACPI_CONFIGFS is not set ++CONFIG_ACPI_IORT=y ++CONFIG_ACPI_GTDT=y ++CONFIG_ACPI_PPTT=y ++# CONFIG_PMIC_OPREGION is not set ++CONFIG_IRQ_BYPASS_MANAGER=y ++CONFIG_VIRTUALIZATION=y + CONFIG_KVM=y ++CONFIG_HAVE_KVM_IRQCHIP=y ++CONFIG_HAVE_KVM_IRQFD=y ++CONFIG_HAVE_KVM_IRQ_ROUTING=y ++CONFIG_HAVE_KVM_EVENTFD=y ++CONFIG_KVM_MMIO=y ++CONFIG_HAVE_KVM_MSI=y ++CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT=y ++CONFIG_KVM_VFIO=y ++CONFIG_HAVE_KVM_ARCH_TLB_FLUSH_ALL=y ++CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT=y ++CONFIG_HAVE_KVM_IRQ_BYPASS=y ++CONFIG_HAVE_KVM_VCPU_RUN_PID_CHANGE=y ++CONFIG_KVM_ARM_PMU=y + CONFIG_TEGRA_DTC_SUPPRESS_WARNINGS=y + CONFIG_ARM64_CRYPTO=y ++CONFIG_CRYPTO_SHA256_ARM64=m ++CONFIG_CRYPTO_SHA512_ARM64=m + CONFIG_CRYPTO_SHA1_ARM64_CE=m + CONFIG_CRYPTO_SHA2_ARM64_CE=m + CONFIG_CRYPTO_SHA512_ARM64_CE=m + CONFIG_CRYPTO_SHA3_ARM64=m + CONFIG_CRYPTO_SM3_ARM64_CE=m ++# CONFIG_CRYPTO_SM4_ARM64_CE is not set + CONFIG_CRYPTO_GHASH_ARM64_CE=m ++# CONFIG_CRYPTO_CRCT10DIF_ARM64_CE is not set ++CONFIG_CRYPTO_AES_ARM64=m ++CONFIG_CRYPTO_AES_ARM64_CE=m + CONFIG_CRYPTO_AES_ARM64_CE_CCM=m + CONFIG_CRYPTO_AES_ARM64_CE_BLK=m + CONFIG_CRYPTO_AES_ARM64_NEON_BLK=m ++# CONFIG_CRYPTO_CHACHA20_NEON is not set ++# CONFIG_CRYPTO_POLY1305_NEON is not set ++# CONFIG_CRYPTO_NHPOLY1305_NEON is not set ++# CONFIG_CRYPTO_AES_ARM64_BS is not set ++# CONFIG_ARCH_TEGRA_18x_SOC is not set ++# CONFIG_ARCH_TEGRA_19x_SOC is not set ++# CONFIG_ARCH_TEGRA_21x_SOC is not set + CONFIG_ARCH_TEGRA_23x_SOC=y +-CONFIG_ARCH_TEGRA_239_SOC=y ++ ++# ++# General architecture-dependent options ++# ++CONFIG_CRASH_CORE=y ++CONFIG_KEXEC_CORE=y ++CONFIG_SET_FS=y ++# CONFIG_KPROBES is not set + CONFIG_JUMP_LABEL=y ++# CONFIG_STATIC_KEYS_SELFTEST is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_FUNCTION_ERROR_INJECTION=y ++CONFIG_HAVE_NMI=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_ARCH_HAS_FORTIFY_SOURCE=y ++CONFIG_ARCH_HAS_KEEPINITRD=y ++CONFIG_ARCH_HAS_SET_MEMORY=y ++CONFIG_ARCH_HAS_SET_DIRECT_MAP=y ++CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y ++CONFIG_HAVE_ASM_MODVERSIONS=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_RSEQ=y ++CONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y ++CONFIG_HAVE_HW_BREAKPOINT=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y ++CONFIG_MMU_GATHER_TABLE_FREE=y ++CONFIG_MMU_GATHER_RCU_TABLE_FREE=y ++CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y ++CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y ++CONFIG_HAVE_CMPXCHG_LOCAL=y ++CONFIG_HAVE_CMPXCHG_DOUBLE=y ++CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_SECCOMP=y ++CONFIG_SECCOMP_FILTER=y ++CONFIG_HAVE_ARCH_STACKLEAK=y ++CONFIG_HAVE_STACKPROTECTOR=y ++CONFIG_STACKPROTECTOR=y ++CONFIG_STACKPROTECTOR_STRONG=y ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOVE_PMD=y ++CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y ++CONFIG_HAVE_ARCH_HUGE_VMAP=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_RELA=y ++CONFIG_ARCH_HAS_ELF_RANDOMIZE=y ++CONFIG_HAVE_ARCH_MMAP_RND_BITS=y ++CONFIG_ARCH_MMAP_RND_BITS=18 ++CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS=y ++CONFIG_ARCH_MMAP_RND_COMPAT_BITS=11 ++CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_COMPAT_OLD_SIGACTION=y ++CONFIG_COMPAT_32BIT_TIME=y ++CONFIG_HAVE_ARCH_VMAP_STACK=y ++CONFIG_VMAP_STACK=y ++CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y ++CONFIG_STRICT_KERNEL_RWX=y ++CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y ++CONFIG_STRICT_MODULE_RWX=y ++CONFIG_HAVE_ARCH_COMPILER_H=y ++CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y ++CONFIG_ARCH_USE_MEMREMAP_PROT=y ++# CONFIG_LOCK_EVENT_COUNTS is not set ++CONFIG_ARCH_HAS_RELR=y ++CONFIG_ARCH_WANT_LD_ORPHAN_WARN=y ++ ++# ++# GCOV-based kernel profiling ++# ++# CONFIG_GCOV_KERNEL is not set ++CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y ++# end of GCOV-based kernel profiling ++ ++CONFIG_HAVE_GCC_PLUGINS=y ++CONFIG_GCC_PLUGINS=y ++# CONFIG_GCC_PLUGIN_CYC_COMPLEXITY is not set ++# CONFIG_GCC_PLUGIN_LATENT_ENTROPY is not set ++# CONFIG_GCC_PLUGIN_RANDSTRUCT is not set ++# end of General architecture-dependent options ++ ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULE_SIG_FORMAT=y + CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set + CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set + CONFIG_MODVERSIONS=y ++CONFIG_ASM_MODVERSIONS=y ++# CONFIG_MODULE_SRCVERSION_ALL is not set + CONFIG_MODULE_SIG=y ++# CONFIG_MODULE_SIG_FORCE is not set ++CONFIG_MODULE_SIG_ALL=y ++# CONFIG_MODULE_SIG_SHA1 is not set ++# CONFIG_MODULE_SIG_SHA224 is not set ++# CONFIG_MODULE_SIG_SHA256 is not set ++# CONFIG_MODULE_SIG_SHA384 is not set + CONFIG_MODULE_SIG_SHA512=y ++CONFIG_MODULE_SIG_HASH="sha512" ++# CONFIG_MODULE_COMPRESS is not set ++# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_TRIM_UNUSED_KSYMS is not set ++CONFIG_MODULES_TREE_LOOKUP=y ++CONFIG_BLOCK=y ++CONFIG_BLK_SCSI_REQUEST=y ++CONFIG_BLK_CGROUP_RWSTAT=y ++CONFIG_BLK_DEV_BSG=y ++CONFIG_BLK_DEV_BSGLIB=y ++CONFIG_BLK_DEV_INTEGRITY=y ++CONFIG_BLK_DEV_INTEGRITY_T10=y ++# CONFIG_BLK_DEV_ZONED is not set + CONFIG_BLK_DEV_THROTTLING=y ++# CONFIG_BLK_DEV_THROTTLING_LOW is not set ++# CONFIG_BLK_CMDLINE_PARSER is not set ++# CONFIG_BLK_WBT is not set ++# CONFIG_BLK_CGROUP_IOLATENCY is not set ++# CONFIG_BLK_CGROUP_IOCOST is not set ++CONFIG_BLK_DEBUG_FS=y ++# CONFIG_BLK_SED_OPAL is not set ++# CONFIG_BLK_INLINE_ENCRYPTION is not set ++ ++# ++# Partition Types ++# + CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++# CONFIG_CMDLINE_PARTITION is not set ++# end of Partition Types ++ ++CONFIG_BLOCK_COMPAT=y ++CONFIG_BLK_MQ_PCI=y ++CONFIG_BLK_MQ_VIRTIO=y ++CONFIG_BLK_MQ_RDMA=y ++CONFIG_BLK_PM=y ++ ++# ++# IO Schedulers ++# ++CONFIG_MQ_IOSCHED_DEADLINE=y ++CONFIG_MQ_IOSCHED_KYBER=y ++# CONFIG_IOSCHED_BFQ is not set ++# end of IO Schedulers ++ ++CONFIG_PREEMPT_NOTIFIERS=y ++CONFIG_ASN1=y ++CONFIG_UNINLINE_SPIN_UNLOCK=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_LOCK_SPIN_ON_OWNER=y ++CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y ++CONFIG_QUEUED_SPINLOCKS=y ++CONFIG_ARCH_USE_QUEUED_RWLOCKS=y ++CONFIG_QUEUED_RWLOCKS=y ++CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE=y ++CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y ++CONFIG_FREEZER=y ++ ++# ++# Executable file formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_COMPAT_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_STATE=y ++CONFIG_ARCH_HAVE_ELF_PROT=y ++CONFIG_ARCH_USE_GNU_PROPERTY=y ++CONFIG_ELFCORE=y + # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y + CONFIG_BINFMT_MISC=m ++CONFIG_COREDUMP=y ++# end of Executable file formats ++ ++# ++# Memory Management options ++# ++CONFIG_SELECT_MEMORY_MODEL=y ++# CONFIG_FLATMEM_MANUAL is not set ++CONFIG_SPARSEMEM_MANUAL=y ++CONFIG_SPARSEMEM=y ++CONFIG_SPARSEMEM_EXTREME=y ++CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y ++CONFIG_SPARSEMEM_VMEMMAP=y ++CONFIG_HAVE_FAST_GUP=y ++CONFIG_ARCH_KEEP_MEMBLOCK=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_MEMORY_HOTPLUG is not set ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_MEMORY_BALLOON=y ++CONFIG_BALLOON_COMPACTION=y ++CONFIG_COMPACTION=y ++CONFIG_PAGE_REPORTING=y ++CONFIG_MIGRATION=y ++CONFIG_CONTIG_ALLOC=y ++CONFIG_PHYS_ADDR_T_64BIT=y ++CONFIG_BOUNCE=y ++CONFIG_MMU_NOTIFIER=y + CONFIG_KSM=y + CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 ++CONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y + CONFIG_MEMORY_FAILURE=y ++# CONFIG_HWPOISON_INJECT is not set + CONFIG_TRANSPARENT_HUGEPAGE=y ++CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y ++# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set + CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++# CONFIG_CMA_DEBUGFS is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set + CONFIG_ZSMALLOC=y ++# CONFIG_ZSMALLOC_STAT is not set ++CONFIG_GENERIC_EARLY_IOREMAP=y ++# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set ++# CONFIG_IDLE_PAGE_TRACKING is not set ++CONFIG_ARCH_HAS_PTE_DEVMAP=y ++CONFIG_HMM_MIRROR=y ++CONFIG_FRAME_VECTOR=y ++CONFIG_ARCH_USES_HIGH_VMA_FLAGS=y ++# CONFIG_PERCPU_STATS is not set ++# CONFIG_GUP_BENCHMARK is not set ++# CONFIG_READ_ONLY_THP_FOR_FS is not set ++CONFIG_ARCH_HAS_PTE_SPECIAL=y ++# end of Memory Management options ++ + CONFIG_NET=y ++CONFIG_COMPAT_NETLINK_MESSAGES=y ++CONFIG_NET_INGRESS=y ++CONFIG_NET_EGRESS=y ++CONFIG_SKB_EXTENSIONS=y ++ ++# ++# Networking options ++# + CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set + CONFIG_UNIX=y ++CONFIG_UNIX_SCM=y ++# CONFIG_UNIX_DIAG is not set ++# CONFIG_TLS is not set ++CONFIG_XFRM=y ++CONFIG_XFRM_ALGO=y + CONFIG_XFRM_USER=y ++# CONFIG_XFRM_INTERFACE is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++CONFIG_XFRM_AH=m ++CONFIG_XFRM_ESP=m ++CONFIG_XFRM_IPCOMP=m + CONFIG_NET_KEY=y ++# CONFIG_NET_KEY_MIGRATE is not set ++# CONFIG_SMC is not set ++# CONFIG_XDP_SOCKETS is not set + CONFIG_INET=y + CONFIG_IP_MULTICAST=y + CONFIG_IP_ADVANCED_ROUTER=y ++# CONFIG_IP_FIB_TRIE_STATS is not set + CONFIG_IP_MULTIPLE_TABLES=y ++# CONFIG_IP_ROUTE_MULTIPATH is not set ++# CONFIG_IP_ROUTE_VERBOSE is not set + CONFIG_IP_PNP=y + CONFIG_IP_PNP_DHCP=y + CONFIG_IP_PNP_BOOTP=y ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set + CONFIG_NET_IPGRE_DEMUX=m ++CONFIG_NET_IP_TUNNEL=y ++# CONFIG_NET_IPGRE is not set ++# CONFIG_IP_MROUTE is not set + CONFIG_SYN_COOKIES=y ++# CONFIG_NET_IPVTI is not set ++CONFIG_NET_UDP_TUNNEL=y ++# CONFIG_NET_FOU is not set ++# CONFIG_NET_FOU_IP_TUNNELS is not set ++# CONFIG_INET_AH is not set + CONFIG_INET_ESP=m ++# CONFIG_INET_ESP_OFFLOAD is not set ++# CONFIG_INET_ESPINTCP is not set ++# CONFIG_INET_IPCOMP is not set ++CONFIG_INET_TUNNEL=m + CONFIG_INET_DIAG=m ++CONFIG_INET_TCP_DIAG=m ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_INET_RAW_DIAG is not set ++# CONFIG_INET_DIAG_DESTROY is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y + CONFIG_IPV6_ROUTER_PREF=y + CONFIG_IPV6_ROUTE_INFO=y + CONFIG_IPV6_OPTIMISTIC_DAD=y + CONFIG_INET6_AH=m + CONFIG_INET6_ESP=m ++# CONFIG_INET6_ESP_OFFLOAD is not set ++# CONFIG_INET6_ESPINTCP is not set + CONFIG_INET6_IPCOMP=m + CONFIG_IPV6_MIP6=m ++# CONFIG_IPV6_ILA is not set ++CONFIG_INET6_XFRM_TUNNEL=m ++CONFIG_INET6_TUNNEL=m ++# CONFIG_IPV6_VTI is not set + CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y + CONFIG_IPV6_TUNNEL=m ++# CONFIG_IPV6_GRE is not set + CONFIG_IPV6_MULTIPLE_TABLES=y ++# CONFIG_IPV6_SUBTREES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_IPV6_SEG6_LWTUNNEL is not set ++# CONFIG_IPV6_SEG6_HMAC is not set ++# CONFIG_IPV6_RPL_LWTUNNEL is not set ++# CONFIG_NETLABEL is not set ++# CONFIG_MPTCP is not set ++CONFIG_NETWORK_SECMARK=y ++CONFIG_NET_PTP_CLASSIFY=y ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set + CONFIG_NETFILTER=y ++CONFIG_NETFILTER_ADVANCED=y + CONFIG_BRIDGE_NETFILTER=m ++ ++# ++# Core Netfilter Configuration ++# ++CONFIG_NETFILTER_INGRESS=y ++CONFIG_NETFILTER_NETLINK=m ++CONFIG_NETFILTER_FAMILY_BRIDGE=y ++CONFIG_NETFILTER_FAMILY_ARP=y + CONFIG_NETFILTER_NETLINK_ACCT=m + CONFIG_NETFILTER_NETLINK_QUEUE=m + CONFIG_NETFILTER_NETLINK_LOG=m ++# CONFIG_NETFILTER_NETLINK_OSF is not set + CONFIG_NF_CONNTRACK=m ++CONFIG_NF_LOG_COMMON=m ++# CONFIG_NF_LOG_NETDEV is not set ++CONFIG_NETFILTER_CONNCOUNT=m ++CONFIG_NF_CONNTRACK_MARK=y ++# CONFIG_NF_CONNTRACK_SECMARK is not set ++# CONFIG_NF_CONNTRACK_ZONES is not set ++CONFIG_NF_CONNTRACK_PROCFS=y + CONFIG_NF_CONNTRACK_EVENTS=y ++# CONFIG_NF_CONNTRACK_TIMEOUT is not set ++# CONFIG_NF_CONNTRACK_TIMESTAMP is not set ++# CONFIG_NF_CONNTRACK_LABELS is not set ++CONFIG_NF_CT_PROTO_DCCP=y ++CONFIG_NF_CT_PROTO_GRE=y ++CONFIG_NF_CT_PROTO_SCTP=y ++CONFIG_NF_CT_PROTO_UDPLITE=y + CONFIG_NF_CONNTRACK_AMANDA=m + CONFIG_NF_CONNTRACK_FTP=m + CONFIG_NF_CONNTRACK_H323=m + CONFIG_NF_CONNTRACK_IRC=m ++CONFIG_NF_CONNTRACK_BROADCAST=m + CONFIG_NF_CONNTRACK_NETBIOS_NS=m ++# CONFIG_NF_CONNTRACK_SNMP is not set + CONFIG_NF_CONNTRACK_PPTP=m + CONFIG_NF_CONNTRACK_SANE=m + CONFIG_NF_CONNTRACK_SIP=m + CONFIG_NF_CONNTRACK_TFTP=m + CONFIG_NF_CT_NETLINK=m ++# CONFIG_NETFILTER_NETLINK_GLUE_CT is not set ++CONFIG_NF_NAT=m ++CONFIG_NF_NAT_AMANDA=m ++CONFIG_NF_NAT_FTP=m ++CONFIG_NF_NAT_IRC=m ++CONFIG_NF_NAT_SIP=m ++CONFIG_NF_NAT_TFTP=m ++CONFIG_NF_NAT_REDIRECT=y ++CONFIG_NF_NAT_MASQUERADE=y ++# CONFIG_NF_TABLES is not set ++CONFIG_NETFILTER_XTABLES=m ++ ++# ++# Xtables combined modules ++# ++CONFIG_NETFILTER_XT_MARK=m ++CONFIG_NETFILTER_XT_CONNMARK=m ++ ++# ++# Xtables targets ++# ++# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set + CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m + CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m + CONFIG_NETFILTER_XT_TARGET_CONNMARK=m ++# CONFIG_NETFILTER_XT_TARGET_CT is not set ++# CONFIG_NETFILTER_XT_TARGET_DSCP is not set ++# CONFIG_NETFILTER_XT_TARGET_HL is not set ++# CONFIG_NETFILTER_XT_TARGET_HMARK is not set + CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m ++# CONFIG_NETFILTER_XT_TARGET_LED is not set + CONFIG_NETFILTER_XT_TARGET_LOG=m + CONFIG_NETFILTER_XT_TARGET_MARK=m ++CONFIG_NETFILTER_XT_NAT=m ++CONFIG_NETFILTER_XT_TARGET_NETMAP=m ++# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set ++# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set ++# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set ++# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set ++CONFIG_NETFILTER_XT_TARGET_REDIRECT=m ++CONFIG_NETFILTER_XT_TARGET_MASQUERADE=m + CONFIG_NETFILTER_XT_TARGET_TEE=m + CONFIG_NETFILTER_XT_TARGET_TPROXY=m + CONFIG_NETFILTER_XT_TARGET_TRACE=m ++# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set + CONFIG_NETFILTER_XT_TARGET_TCPMSS=m ++# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set ++ ++# ++# Xtables matches ++# + CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m ++# CONFIG_NETFILTER_XT_MATCH_BPF is not set ++# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set ++# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set + CONFIG_NETFILTER_XT_MATCH_COMMENT=m + CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m ++# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set + CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m + CONFIG_NETFILTER_XT_MATCH_CONNMARK=m + CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m ++# CONFIG_NETFILTER_XT_MATCH_CPU is not set ++# CONFIG_NETFILTER_XT_MATCH_DCCP is not set ++# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set ++# CONFIG_NETFILTER_XT_MATCH_DSCP is not set ++CONFIG_NETFILTER_XT_MATCH_ECN=m ++# CONFIG_NETFILTER_XT_MATCH_ESP is not set + CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m + CONFIG_NETFILTER_XT_MATCH_HELPER=m ++CONFIG_NETFILTER_XT_MATCH_HL=m ++# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set + CONFIG_NETFILTER_XT_MATCH_IPRANGE=m + CONFIG_NETFILTER_XT_MATCH_IPVS=m ++# CONFIG_NETFILTER_XT_MATCH_L2TP is not set + CONFIG_NETFILTER_XT_MATCH_LENGTH=m + CONFIG_NETFILTER_XT_MATCH_LIMIT=m + CONFIG_NETFILTER_XT_MATCH_MAC=m + CONFIG_NETFILTER_XT_MATCH_MARK=m + CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m ++# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set ++# CONFIG_NETFILTER_XT_MATCH_OSF is not set + CONFIG_NETFILTER_XT_MATCH_OWNER=m ++# CONFIG_NETFILTER_XT_MATCH_POLICY is not set ++# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set + CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m + CONFIG_NETFILTER_XT_MATCH_QUOTA=m ++# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set ++# CONFIG_NETFILTER_XT_MATCH_REALM is not set + CONFIG_NETFILTER_XT_MATCH_RECENT=m ++# CONFIG_NETFILTER_XT_MATCH_SCTP is not set + CONFIG_NETFILTER_XT_MATCH_SOCKET=m + CONFIG_NETFILTER_XT_MATCH_STATE=m + CONFIG_NETFILTER_XT_MATCH_STATISTIC=m + CONFIG_NETFILTER_XT_MATCH_STRING=m ++# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set + CONFIG_NETFILTER_XT_MATCH_TIME=m + CONFIG_NETFILTER_XT_MATCH_U32=m ++# end of Core Netfilter Configuration ++ ++# CONFIG_IP_SET is not set + CONFIG_IP_VS=m ++# CONFIG_IP_VS_IPV6 is not set ++# CONFIG_IP_VS_DEBUG is not set ++CONFIG_IP_VS_TAB_BITS=12 ++ ++# ++# IPVS transport protocol load balancing support ++# + CONFIG_IP_VS_PROTO_TCP=y + CONFIG_IP_VS_PROTO_UDP=y ++# CONFIG_IP_VS_PROTO_ESP is not set ++# CONFIG_IP_VS_PROTO_AH is not set ++# CONFIG_IP_VS_PROTO_SCTP is not set ++ ++# ++# IPVS scheduler ++# + CONFIG_IP_VS_RR=m ++# CONFIG_IP_VS_WRR is not set ++# CONFIG_IP_VS_LC is not set ++# CONFIG_IP_VS_WLC is not set ++# CONFIG_IP_VS_FO is not set ++# CONFIG_IP_VS_OVF is not set ++# CONFIG_IP_VS_LBLC is not set ++# CONFIG_IP_VS_LBLCR is not set ++# CONFIG_IP_VS_DH is not set ++# CONFIG_IP_VS_SH is not set ++# CONFIG_IP_VS_MH is not set ++# CONFIG_IP_VS_SED is not set ++# CONFIG_IP_VS_NQ is not set ++ ++# ++# IPVS SH scheduler ++# ++CONFIG_IP_VS_SH_TAB_BITS=8 ++ ++# ++# IPVS MH scheduler ++# ++CONFIG_IP_VS_MH_TAB_INDEX=12 ++ ++# ++# IPVS application helper ++# ++# CONFIG_IP_VS_FTP is not set + CONFIG_IP_VS_NFCT=y ++# CONFIG_IP_VS_PE_SIP is not set ++ ++# ++# IP: Netfilter Configuration ++# ++CONFIG_NF_DEFRAG_IPV4=m ++CONFIG_NF_SOCKET_IPV4=m ++CONFIG_NF_TPROXY_IPV4=m ++CONFIG_NF_DUP_IPV4=m ++# CONFIG_NF_LOG_ARP is not set ++CONFIG_NF_LOG_IPV4=m ++CONFIG_NF_REJECT_IPV4=m ++CONFIG_NF_NAT_PPTP=m ++CONFIG_NF_NAT_H323=m + CONFIG_IP_NF_IPTABLES=m + CONFIG_IP_NF_MATCH_AH=m + CONFIG_IP_NF_MATCH_ECN=m +@@ -203,89 +1285,326 @@ CONFIG_IP_NF_MATCH_RPFILTER=m + CONFIG_IP_NF_MATCH_TTL=m + CONFIG_IP_NF_FILTER=m + CONFIG_IP_NF_TARGET_REJECT=m ++# CONFIG_IP_NF_TARGET_SYNPROXY is not set + CONFIG_IP_NF_NAT=m + CONFIG_IP_NF_TARGET_MASQUERADE=m + CONFIG_IP_NF_TARGET_NETMAP=m + CONFIG_IP_NF_TARGET_REDIRECT=m + CONFIG_IP_NF_MANGLE=m ++# CONFIG_IP_NF_TARGET_CLUSTERIP is not set ++# CONFIG_IP_NF_TARGET_ECN is not set ++# CONFIG_IP_NF_TARGET_TTL is not set + CONFIG_IP_NF_RAW=m + CONFIG_IP_NF_SECURITY=m + CONFIG_IP_NF_ARPTABLES=m + CONFIG_IP_NF_ARPFILTER=m + CONFIG_IP_NF_ARP_MANGLE=m ++# end of IP: Netfilter Configuration ++ ++# ++# IPv6: Netfilter Configuration ++# ++CONFIG_NF_SOCKET_IPV6=m ++CONFIG_NF_TPROXY_IPV6=m ++CONFIG_NF_DUP_IPV6=m ++CONFIG_NF_REJECT_IPV6=m ++CONFIG_NF_LOG_IPV6=m + CONFIG_IP6_NF_IPTABLES=m ++# CONFIG_IP6_NF_MATCH_AH is not set ++# CONFIG_IP6_NF_MATCH_EUI64 is not set ++# CONFIG_IP6_NF_MATCH_FRAG is not set ++# CONFIG_IP6_NF_MATCH_OPTS is not set ++# CONFIG_IP6_NF_MATCH_HL is not set ++# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set ++# CONFIG_IP6_NF_MATCH_MH is not set ++# CONFIG_IP6_NF_MATCH_RPFILTER is not set ++# CONFIG_IP6_NF_MATCH_RT is not set ++# CONFIG_IP6_NF_MATCH_SRH is not set ++# CONFIG_IP6_NF_TARGET_HL is not set + CONFIG_IP6_NF_FILTER=m + CONFIG_IP6_NF_TARGET_REJECT=m ++# CONFIG_IP6_NF_TARGET_SYNPROXY is not set + CONFIG_IP6_NF_MANGLE=m + CONFIG_IP6_NF_RAW=m ++# CONFIG_IP6_NF_SECURITY is not set + CONFIG_IP6_NF_NAT=m + CONFIG_IP6_NF_TARGET_MASQUERADE=m ++# CONFIG_IP6_NF_TARGET_NPT is not set ++# end of IPv6: Netfilter Configuration ++ ++CONFIG_NF_DEFRAG_IPV6=m ++# CONFIG_NF_CONNTRACK_BRIDGE is not set ++# CONFIG_BRIDGE_NF_EBTABLES is not set ++# CONFIG_BPFILTER is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++CONFIG_STP=y ++CONFIG_GARP=m ++CONFIG_MRP=m + CONFIG_BRIDGE=y ++CONFIG_BRIDGE_IGMP_SNOOPING=y + CONFIG_BRIDGE_VLAN_FILTERING=y ++# CONFIG_BRIDGE_MRP is not set ++CONFIG_HAVE_NET_DSA=y + CONFIG_NET_DSA=m ++# CONFIG_NET_DSA_TAG_AR9331 is not set ++# CONFIG_NET_DSA_TAG_BRCM is not set ++# CONFIG_NET_DSA_TAG_BRCM_PREPEND is not set ++# CONFIG_NET_DSA_TAG_GSWIP is not set ++# CONFIG_NET_DSA_TAG_DSA is not set ++# CONFIG_NET_DSA_TAG_EDSA is not set ++# CONFIG_NET_DSA_TAG_MTK is not set ++# CONFIG_NET_DSA_TAG_KSZ is not set ++# CONFIG_NET_DSA_TAG_RTL4_A is not set + CONFIG_NET_DSA_TAG_OCELOT=m ++# CONFIG_NET_DSA_TAG_QCA is not set ++# CONFIG_NET_DSA_TAG_LAN9303 is not set ++# CONFIG_NET_DSA_TAG_SJA1105 is not set ++# CONFIG_NET_DSA_TAG_TRAILER is not set + CONFIG_VLAN_8021Q=m + CONFIG_VLAN_8021Q_GVRP=y + CONFIG_VLAN_8021Q_MVRP=y ++# CONFIG_DECNET is not set ++CONFIG_LLC=y ++# CONFIG_LLC2 is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set + CONFIG_NET_SCHED=y ++ ++# ++# Queueing/Scheduling ++# ++# CONFIG_NET_SCH_CBQ is not set + CONFIG_NET_SCH_HTB=y ++# CONFIG_NET_SCH_HFSC is not set ++# CONFIG_NET_SCH_PRIO is not set ++# CONFIG_NET_SCH_MULTIQ is not set ++# CONFIG_NET_SCH_RED is not set ++# CONFIG_NET_SCH_SFB is not set ++# CONFIG_NET_SCH_SFQ is not set ++# CONFIG_NET_SCH_TEQL is not set ++# CONFIG_NET_SCH_TBF is not set + CONFIG_NET_SCH_CBS=y + CONFIG_NET_SCH_ETF=m + CONFIG_NET_SCH_TAPRIO=m ++# CONFIG_NET_SCH_GRED is not set ++# CONFIG_NET_SCH_DSMARK is not set ++# CONFIG_NET_SCH_NETEM is not set ++# CONFIG_NET_SCH_DRR is not set + CONFIG_NET_SCH_MQPRIO=m ++# CONFIG_NET_SCH_SKBPRIO is not set ++# CONFIG_NET_SCH_CHOKE is not set ++# CONFIG_NET_SCH_QFQ is not set ++# CONFIG_NET_SCH_CODEL is not set ++# CONFIG_NET_SCH_FQ_CODEL is not set ++# CONFIG_NET_SCH_CAKE is not set ++# CONFIG_NET_SCH_FQ is not set ++# CONFIG_NET_SCH_HHF is not set ++# CONFIG_NET_SCH_PIE is not set + CONFIG_NET_SCH_INGRESS=m ++# CONFIG_NET_SCH_PLUG is not set ++# CONFIG_NET_SCH_ETS is not set ++# CONFIG_NET_SCH_DEFAULT is not set ++ ++# ++# Classification ++# ++CONFIG_NET_CLS=y + CONFIG_NET_CLS_BASIC=m ++# CONFIG_NET_CLS_TCINDEX is not set ++# CONFIG_NET_CLS_ROUTE4 is not set ++# CONFIG_NET_CLS_FW is not set + CONFIG_NET_CLS_U32=y ++# CONFIG_CLS_U32_PERF is not set ++# CONFIG_CLS_U32_MARK is not set ++# CONFIG_NET_CLS_RSVP is not set ++# CONFIG_NET_CLS_RSVP6 is not set ++# CONFIG_NET_CLS_FLOW is not set + CONFIG_NET_CLS_CGROUP=y ++# CONFIG_NET_CLS_BPF is not set + CONFIG_NET_CLS_FLOWER=m ++# CONFIG_NET_CLS_MATCHALL is not set + CONFIG_NET_EMATCH=y ++CONFIG_NET_EMATCH_STACK=32 ++# CONFIG_NET_EMATCH_CMP is not set ++# CONFIG_NET_EMATCH_NBYTE is not set + CONFIG_NET_EMATCH_U32=y ++# CONFIG_NET_EMATCH_META is not set ++# CONFIG_NET_EMATCH_TEXT is not set ++# CONFIG_NET_EMATCH_CANID is not set ++# CONFIG_NET_EMATCH_IPT is not set + CONFIG_NET_CLS_ACT=y + CONFIG_NET_ACT_POLICE=m + CONFIG_NET_ACT_GACT=m ++# CONFIG_GACT_PROB is not set + CONFIG_NET_ACT_MIRRED=m ++# CONFIG_NET_ACT_SAMPLE is not set ++# CONFIG_NET_ACT_IPT is not set ++# CONFIG_NET_ACT_NAT is not set ++# CONFIG_NET_ACT_PEDIT is not set ++# CONFIG_NET_ACT_SIMP is not set ++# CONFIG_NET_ACT_SKBEDIT is not set ++# CONFIG_NET_ACT_CSUM is not set ++# CONFIG_NET_ACT_MPLS is not set ++# CONFIG_NET_ACT_VLAN is not set ++# CONFIG_NET_ACT_BPF is not set ++# CONFIG_NET_ACT_CONNMARK is not set ++# CONFIG_NET_ACT_CTINFO is not set ++# CONFIG_NET_ACT_SKBMOD is not set ++# CONFIG_NET_ACT_IFE is not set ++# CONFIG_NET_ACT_TUNNEL_KEY is not set + CONFIG_NET_ACT_GATE=m ++# CONFIG_NET_TC_SKB_EXT is not set ++CONFIG_NET_SCH_FIFO=y ++# CONFIG_DCB is not set ++CONFIG_DNS_RESOLVER=y ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_MPLS is not set ++# CONFIG_NET_NSH is not set ++# CONFIG_HSR is not set ++CONFIG_NET_SWITCHDEV=y ++CONFIG_NET_L3_MASTER_DEV=y ++# CONFIG_QRTR is not set ++# CONFIG_NET_NCSI is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y + CONFIG_CGROUP_NET_PRIO=y ++CONFIG_CGROUP_NET_CLASSID=y ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y + CONFIG_BPF_JIT=y ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_NET_DROP_MONITOR is not set ++# end of Network testing ++# end of Networking options ++ ++# CONFIG_HAMRADIO is not set + CONFIG_CAN=m ++CONFIG_CAN_RAW=m ++CONFIG_CAN_BCM=m ++CONFIG_CAN_GW=m ++# CONFIG_CAN_J1939 is not set ++# CONFIG_CAN_ISOTP is not set ++ ++# ++# CAN Device Drivers ++# + CONFIG_CAN_VCAN=m ++# CONFIG_CAN_VXCAN is not set + CONFIG_CAN_SLCAN=m ++CONFIG_CAN_DEV=m ++CONFIG_CAN_CALC_BITTIMING=y ++# CONFIG_CAN_FLEXCAN is not set ++# CONFIG_CAN_GRCAN is not set ++# CONFIG_CAN_KVASER_PCIEFD is not set ++# CONFIG_CAN_XILINXCAN is not set + CONFIG_CAN_C_CAN=m ++# CONFIG_CAN_C_CAN_PLATFORM is not set ++# CONFIG_CAN_C_CAN_PCI is not set + CONFIG_CAN_CC770=m + CONFIG_CAN_CC770_ISA=m + CONFIG_CAN_CC770_PLATFORM=m ++# CONFIG_CAN_IFI_CANFD is not set + CONFIG_CAN_M_CAN=m ++# CONFIG_CAN_M_CAN_PLATFORM is not set ++# CONFIG_CAN_M_CAN_TCAN4X5X is not set ++# CONFIG_CAN_PEAK_PCIEFD is not set + CONFIG_CAN_SJA1000=m + CONFIG_CAN_EMS_PCI=m ++# CONFIG_CAN_F81601 is not set + CONFIG_CAN_KVASER_PCI=m ++# CONFIG_CAN_PEAK_PCI is not set + CONFIG_CAN_PLX_PCI=m + CONFIG_CAN_SJA1000_ISA=m + CONFIG_CAN_SJA1000_PLATFORM=m + CONFIG_CAN_SOFTING=m ++ ++# ++# CAN SPI interfaces ++# ++# CONFIG_CAN_HI311X is not set + CONFIG_CAN_MCP251X=m ++# CONFIG_CAN_MCP251XFD is not set ++# end of CAN SPI interfaces ++ ++# ++# CAN USB interfaces ++# + CONFIG_CAN_8DEV_USB=m + CONFIG_CAN_EMS_USB=m + CONFIG_CAN_ESD_USB2=m + CONFIG_CAN_GS_USB=m + CONFIG_CAN_KVASER_USB=m ++# CONFIG_CAN_MCBA_USB is not set + CONFIG_CAN_PEAK_USB=m ++# CONFIG_CAN_UCAN is not set ++# end of CAN USB interfaces ++ ++# CONFIG_CAN_DEBUG_DEVICES is not set ++# end of CAN Device Drivers ++ + CONFIG_MTTCAN=m + CONFIG_TEGRA_HV_SECCAN=m + CONFIG_BT=y ++CONFIG_BT_BREDR=y + CONFIG_BT_RFCOMM=y ++# CONFIG_BT_RFCOMM_TTY is not set + CONFIG_BT_BNEP=m ++# CONFIG_BT_BNEP_MC_FILTER is not set ++# CONFIG_BT_BNEP_PROTO_FILTER is not set + CONFIG_BT_HIDP=y ++# CONFIG_BT_HS is not set + # CONFIG_BT_LE is not set + CONFIG_BT_LEDS=y ++# CONFIG_BT_MSFTEXT is not set + # CONFIG_BT_DEBUGFS is not set ++# CONFIG_BT_SELFTEST is not set ++# CONFIG_BT_FEATURE_DEBUG is not set ++ ++# ++# Bluetooth device drivers ++# ++CONFIG_BT_INTEL=m ++CONFIG_BT_BCM=m ++CONFIG_BT_RTL=m ++CONFIG_BT_QCA=m + CONFIG_BT_HCIBTUSB=m ++# CONFIG_BT_HCIBTUSB_AUTOSUSPEND is not set ++CONFIG_BT_HCIBTUSB_BCM=y ++# CONFIG_BT_HCIBTUSB_MTK is not set ++CONFIG_BT_HCIBTUSB_RTL=y ++# CONFIG_BT_HCIBTSDIO is not set + CONFIG_BT_HCIUART=m ++CONFIG_BT_HCIUART_SERDEV=y ++CONFIG_BT_HCIUART_H4=y ++# CONFIG_BT_HCIUART_NOKIA is not set + CONFIG_BT_HCIUART_BCSP=y + CONFIG_BT_HCIUART_ATH3K=y + CONFIG_BT_HCIUART_LL=y ++# CONFIG_BT_HCIUART_3WIRE is not set + CONFIG_BT_HCIUART_INTEL=y + CONFIG_BT_HCIUART_BCM=y ++# CONFIG_BT_HCIUART_RTL is not set + CONFIG_BT_HCIUART_QCA=y ++# CONFIG_BT_HCIUART_AG6XX is not set ++# CONFIG_BT_HCIUART_MRVL is not set + CONFIG_BT_HCIBCM203X=m + CONFIG_BT_HCIBPA10X=m + CONFIG_BT_HCIBFUSB=m +@@ -293,222 +1612,1218 @@ CONFIG_BT_HCIVHCI=m + CONFIG_BT_MRVL=m + CONFIG_BT_MRVL_SDIO=m + CONFIG_BT_ATH3K=m ++# CONFIG_BT_MTKSDIO is not set ++# CONFIG_BT_MTKUART is not set ++# end of Bluetooth device drivers ++ + CONFIG_AF_RXRPC=m ++# CONFIG_AF_RXRPC_IPV6 is not set ++# CONFIG_AF_RXRPC_INJECT_LOSS is not set ++# CONFIG_AF_RXRPC_DEBUG is not set ++# CONFIG_RXKAD is not set + CONFIG_AF_KCM=m ++CONFIG_STREAM_PARSER=y ++CONFIG_FIB_RULES=y ++CONFIG_WIRELESS=y ++CONFIG_WIRELESS_EXT=y ++CONFIG_WEXT_CORE=y ++CONFIG_WEXT_PROC=y ++CONFIG_WEXT_SPY=y ++CONFIG_WEXT_PRIV=y + CONFIG_CFG80211=m ++# CONFIG_NL80211_TESTMODE is not set ++# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set + CONFIG_CFG80211_CERTIFICATION_ONUS=y + # CONFIG_CFG80211_REQUIRE_SIGNED_REGDB is not set ++# CONFIG_CFG80211_REG_CELLULAR_HINTS is not set ++# CONFIG_CFG80211_REG_RELAX_NO_IR is not set ++CONFIG_CFG80211_DEFAULT_PS=y ++# CONFIG_CFG80211_DEBUGFS is not set ++CONFIG_CFG80211_CRDA_SUPPORT=y ++CONFIG_CFG80211_WEXT=y ++CONFIG_CFG80211_WEXT_EXPORT=y ++CONFIG_LIB80211=m ++CONFIG_LIB80211_CRYPT_WEP=m ++CONFIG_LIB80211_CRYPT_CCMP=m ++CONFIG_LIB80211_CRYPT_TKIP=m ++# CONFIG_LIB80211_DEBUG is not set + CONFIG_MAC80211=m ++CONFIG_MAC80211_HAS_RC=y ++CONFIG_MAC80211_RC_MINSTREL=y ++CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y ++CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" ++# CONFIG_MAC80211_MESH is not set ++CONFIG_MAC80211_LEDS=y ++CONFIG_MAC80211_DEBUGFS=y ++# CONFIG_MAC80211_MESSAGE_TRACING is not set ++# CONFIG_MAC80211_DEBUG_MENU is not set ++CONFIG_MAC80211_STA_HASH_MAX_SIZE=0 ++# CONFIG_WIMAX is not set + CONFIG_RFKILL=y ++CONFIG_RFKILL_LEDS=y ++# CONFIG_RFKILL_INPUT is not set ++# CONFIG_RFKILL_GPIO is not set + CONFIG_NET_9P=y ++# CONFIG_NET_9P_VIRTIO is not set ++# CONFIG_NET_9P_RDMA is not set ++# CONFIG_NET_9P_DEBUG is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set + CONFIG_NFC=m ++# CONFIG_NFC_DIGITAL is not set + CONFIG_NFC_NCI=m ++# CONFIG_NFC_NCI_SPI is not set ++# CONFIG_NFC_NCI_UART is not set ++# CONFIG_NFC_HCI is not set ++ ++# ++# Near Field Communication (NFC) devices ++# ++# CONFIG_NFC_FDP is not set ++# CONFIG_NFC_PN533_USB is not set ++# CONFIG_NFC_PN533_I2C is not set ++# CONFIG_NFC_PN532_UART is not set ++# CONFIG_NFC_MRVL_USB is not set ++# CONFIG_NFC_ST_NCI_I2C is not set ++# CONFIG_NFC_ST_NCI_SPI is not set ++# CONFIG_NFC_NXP_NCI is not set ++CONFIG_NFC_S3FWRN5=m + CONFIG_NFC_S3FWRN5_I2C=m ++# end of Near Field Communication (NFC) devices ++ ++# CONFIG_PSAMPLE is not set ++# CONFIG_NET_IFE is not set ++# CONFIG_LWTUNNEL is not set ++CONFIG_DST_CACHE=y ++CONFIG_GRO_CELLS=y ++CONFIG_NET_DEVLINK=y ++CONFIG_PAGE_POOL=y ++CONFIG_FAILOVER=y ++CONFIG_ETHTOOL_NETLINK=y ++CONFIG_HAVE_EBPF_JIT=y ++ ++# ++# Device Drivers ++# ++CONFIG_ARM_AMBA=y ++CONFIG_TEGRA_AHB=y ++CONFIG_HAVE_PCI=y + CONFIG_PCI=y ++CONFIG_PCI_DOMAINS=y ++CONFIG_PCI_DOMAINS_GENERIC=y ++CONFIG_PCI_SYSCALL=y + CONFIG_PCIEPORTBUS=y ++# CONFIG_HOTPLUG_PCI_PCIE is not set + CONFIG_PCIEAER=y ++# CONFIG_PCIEAER_INJECT is not set + CONFIG_PCIE_ECRC=y ++CONFIG_PCIEASPM=y ++# CONFIG_PCIEASPM_DEFAULT is not set ++# CONFIG_PCIEASPM_POWERSAVE is not set + CONFIG_PCIEASPM_POWER_SUPERSAVE=y +-CONFIG_PCI_STUB=m ++# CONFIG_PCIEASPM_PERFORMANCE is not set ++CONFIG_PCIE_PME=y ++# CONFIG_PCIE_DPC is not set ++# CONFIG_PCIE_PTM is not set ++CONFIG_PCI_MSI=y ++CONFIG_PCI_MSI_IRQ_DOMAIN=y ++CONFIG_PCI_MSI_ARCH_FALLBACKS=y ++CONFIG_PCI_QUIRKS=y ++CONFIG_PCI_DEBUG=y ++# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set ++CONFIG_PCI_STUB=y ++# CONFIG_PCI_PF_STUB is not set ++CONFIG_PCI_ATS=y ++CONFIG_PCI_ECAM=y + CONFIG_PCI_IOV=y ++# CONFIG_PCI_PRI is not set ++# CONFIG_PCI_PASID is not set ++CONFIG_PCI_LABEL=y ++# CONFIG_PCIE_BUS_TUNE_OFF is not set ++# CONFIG_PCIE_BUS_DEFAULT is not set + CONFIG_PCIE_BUS_SAFE=y ++# CONFIG_PCIE_BUS_PERFORMANCE is not set ++# CONFIG_PCIE_BUS_PEER2PEER is not set ++CONFIG_HOTPLUG_PCI=y ++CONFIG_HOTPLUG_PCI_ACPI=y ++# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set ++# CONFIG_HOTPLUG_PCI_CPCI is not set ++# CONFIG_HOTPLUG_PCI_SHPC is not set ++ ++# ++# PCI controller drivers ++# ++# CONFIG_PCI_FTPCI100 is not set + CONFIG_PCI_TEGRA=y ++CONFIG_PCI_HOST_COMMON=y ++CONFIG_PCI_HOST_GENERIC=y ++# CONFIG_PCIE_XILINX is not set ++# CONFIG_PCI_XGENE is not set ++# CONFIG_PCIE_ALTERA is not set ++# CONFIG_PCI_HOST_THUNDER_PEM is not set ++# CONFIG_PCI_HOST_THUNDER_ECAM is not set ++# CONFIG_PCIE_HISI_ERR is not set ++ ++# ++# DesignWare PCI Core Support ++# ++CONFIG_PCIE_DW=y ++CONFIG_PCIE_DW_HOST=y ++CONFIG_PCIE_DW_EP=y ++# CONFIG_PCIE_DW_PLAT_HOST is not set ++# CONFIG_PCIE_DW_PLAT_EP is not set ++# CONFIG_PCI_HISI is not set ++# CONFIG_PCIE_KIRIN is not set ++# CONFIG_PCI_MESON is not set ++CONFIG_PCIE_TEGRA194=y ++CONFIG_PCIE_TEGRA194_HOST=y + CONFIG_PCIE_TEGRA194_EP=y ++# CONFIG_PCIE_RP_DMA_TEST is not set ++# CONFIG_PCIE_AL is not set ++# end of DesignWare PCI Core Support ++ ++# ++# Mobiveil PCIe Core Support ++# ++CONFIG_PCIE_MOBIVEIL=y ++CONFIG_PCIE_MOBIVEIL_HOST=y + CONFIG_PCIE_LAYERSCAPE_GEN4=y ++# end of Mobiveil PCIe Core Support ++ ++# ++# Cadence PCIe controllers support ++# ++# CONFIG_PCIE_CADENCE_PLAT_HOST is not set ++# CONFIG_PCIE_CADENCE_PLAT_EP is not set ++# CONFIG_PCI_J721E_HOST is not set ++# CONFIG_PCI_J721E_EP is not set ++# end of Cadence PCIe controllers support ++# end of PCI controller drivers ++ + CONFIG_PCIE_TEGRA_VF=y ++ ++# ++# PCI Endpoint ++# + CONFIG_PCI_ENDPOINT=y + CONFIG_PCI_ENDPOINT_CONFIGFS=y + CONFIG_PCI_EPF_TEST=y + CONFIG_PCIE_EPF_NV_TEST=y + CONFIG_PCIE_EPF_TEGRA_VNET=y +-CONFIG_PCI_SERIAL_CH384=m ++# CONFIG_PCIE_EPF_DMA_TEST is not set ++# end of PCI Endpoint ++ ++# ++# PCI switch controller drivers ++# ++CONFIG_PCI_SW_SWITCHTEC=m ++# end of PCI switch controller drivers ++ ++# ++# PCI Endpoint ++# ++# CONFIG_PCIE_TEGRA_DW_EP is not set ++# end of PCI Endpoint ++ ++# CONFIG_PCCARD is not set ++# CONFIG_RAPIDIO is not set ++ ++# ++# Generic Driver Options ++# + CONFIG_UEVENT_HELPER=y ++CONFIG_UEVENT_HELPER_PATH="" + CONFIG_DEVTMPFS=y + CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++ ++# ++# Firmware loader ++# ++CONFIG_FW_LOADER=y ++CONFIG_FW_LOADER_PAGED_BUF=y ++CONFIG_EXTRA_FIRMWARE="" + CONFIG_FW_LOADER_USER_HELPER=y + CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y ++# CONFIG_FW_LOADER_COMPRESS is not set ++CONFIG_FW_CACHE=y ++# end of Firmware loader ++ ++CONFIG_WANT_DEV_COREDUMP=y ++CONFIG_ALLOW_DEV_COREDUMP=y ++CONFIG_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set ++# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set ++CONFIG_GENERIC_CPU_AUTOPROBE=y ++CONFIG_GENERIC_CPU_VULNERABILITIES=y ++CONFIG_SOC_BUS=y ++CONFIG_REGMAP=y ++CONFIG_REGMAP_I2C=y ++CONFIG_REGMAP_SPI=y ++CONFIG_REGMAP_SPMI=m ++CONFIG_REGMAP_MMIO=y ++CONFIG_REGMAP_IRQ=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_DMA_FENCE_TRACE is not set ++CONFIG_GENERIC_ARCH_TOPOLOGY=y ++# end of Generic Driver Options ++ ++# ++# Bus devices ++# + CONFIG_BRCMSTB_GISB_ARB=y ++# CONFIG_MOXTET is not set + CONFIG_SIMPLE_PM_BUS=y + CONFIG_TEGRA_ACONNECT=y ++# CONFIG_TEGRA_GMI is not set + CONFIG_VEXPRESS_CONFIG=y ++# CONFIG_MHI_BUS is not set ++# end of Bus devices ++ ++# CONFIG_CONNECTOR is not set ++# CONFIG_GNSS is not set + CONFIG_MTD=m ++# CONFIG_MTD_TESTS is not set ++ ++# ++# Partition parsers ++# ++# CONFIG_MTD_AR7_PARTS is not set + CONFIG_MTD_CMDLINE_PARTS=m ++CONFIG_MTD_OF_PARTS=m ++# CONFIG_MTD_AFS_PARTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# end of Partition parsers ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=m + CONFIG_MTD_BLOCK=m ++# CONFIG_MTD_BLOCK_RO is not set ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++# CONFIG_MTD_PARTITIONED_MASTER is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++# end of RAM/ROM/Flash chip drivers ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_INTEL_VR_NOR is not set ++# CONFIG_MTD_PLATRAM is not set ++# end of Mapping drivers for chip access ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_PMC551 is not set ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_MCHP23K256 is not set ++# CONFIG_MTD_SST25L is not set + CONFIG_MTD_QSPI_FLASH=m ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++# end of Self-contained MTD device drivers ++ + CONFIG_MTD_TEGRA_VIRT=m ++ ++# ++# NAND ++# ++CONFIG_MTD_NAND_CORE=m ++# CONFIG_MTD_ONENAND is not set ++CONFIG_MTD_NAND_ECC_SW_HAMMING=m ++# CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC is not set + CONFIG_MTD_RAW_NAND=m ++# CONFIG_MTD_NAND_ECC_SW_BCH is not set ++ ++# ++# Raw/parallel NAND flash controllers ++# ++# CONFIG_MTD_NAND_DENALI_PCI is not set ++# CONFIG_MTD_NAND_DENALI_DT is not set ++# CONFIG_MTD_NAND_CAFE is not set ++# CONFIG_MTD_NAND_BRCMNAND is not set ++# CONFIG_MTD_NAND_MXIC is not set ++# CONFIG_MTD_NAND_TEGRA is not set ++# CONFIG_MTD_NAND_GPIO is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_MTD_NAND_CADENCE is not set ++# CONFIG_MTD_NAND_ARASAN is not set ++ ++# ++# Misc ++# ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_RICOH is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_SPI_NAND is not set ++ ++# ++# ECC engine support ++# ++CONFIG_MTD_NAND_ECC=y ++# end of ECC engine support ++# end of NAND ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# end of LPDDR & LPDDR2 PCM memory drivers ++ + CONFIG_MTD_SPI_NOR=m ++CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y + CONFIG_MTD_UBI=m ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++# CONFIG_MTD_HYPERBUS is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++# CONFIG_OF_UNITTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_KOBJ=y ++CONFIG_OF_DYNAMIC=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_OF_RESOLVE=y ++CONFIG_OF_OVERLAY=y ++# CONFIG_PARPORT is not set ++CONFIG_PNP=y + # CONFIG_PNP_DEBUG_MESSAGES is not set ++ ++# ++# Protocols ++# ++CONFIG_PNPACPI=y ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set + CONFIG_ZRAM=m ++# CONFIG_ZRAM_WRITEBACK is not set ++# CONFIG_ZRAM_MEMORY_TRACKING is not set ++# CONFIG_BLK_DEV_UMEM is not set + CONFIG_BLK_DEV_LOOP=m ++CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set + CONFIG_BLK_DEV_NBD=m ++# CONFIG_BLK_DEV_SKD is not set ++# CONFIG_BLK_DEV_SX8 is not set + CONFIG_BLK_DEV_RAM=m ++CONFIG_BLK_DEV_RAM_COUNT=16 + CONFIG_BLK_DEV_RAM_SIZE=8192 ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set + CONFIG_VIRTIO_BLK=y ++# CONFIG_BLK_DEV_RBD is not set ++# CONFIG_BLK_DEV_RSXX is not set + CONFIG_TEGRA_HV_BLKDEV=y + CONFIG_TEGRA_HV_BLKDEV_OOPS=y ++ ++# ++# NVME Support ++# ++CONFIG_NVME_CORE=y + CONFIG_BLK_DEV_NVME=y ++# CONFIG_NVME_MULTIPATH is not set ++# CONFIG_NVME_HWMON is not set ++CONFIG_NVME_FABRICS=m + CONFIG_NVME_RDMA=m + CONFIG_NVME_FC=m ++# CONFIG_NVME_TCP is not set ++# CONFIG_NVME_TARGET is not set ++# end of NVME Support ++ ++# ++# Misc devices ++# ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_PHANTOM is not set + CONFIG_TIFM_CORE=m ++CONFIG_TIFM_7XX1=m ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_HP_ILO is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set + CONFIG_SRAM=y + CONFIG_PCI_ENDPOINT_TEST=m ++# CONFIG_XILINX_SDFEC is not set ++# CONFIG_PVPANIC is not set ++# CONFIG_HISI_HIKEY_USB is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# + CONFIG_EEPROM_AT24=m + CONFIG_EEPROM_AT25=m ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++CONFIG_EEPROM_93CX6=m ++# CONFIG_EEPROM_93XX46 is not set ++# CONFIG_EEPROM_IDT_89HPESX is not set ++# CONFIG_EEPROM_EE1004 is not set ++# end of EEPROM support ++ + CONFIG_CB710_CORE=m ++# CONFIG_CB710_DEBUG is not set ++CONFIG_CB710_DEBUG_ASSUMPTIONS=y ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# end of Texas Instruments shared transport line discipline ++ ++# CONFIG_SENSORS_LIS3_I2C is not set ++# CONFIG_ALTERA_STAPL is not set ++# CONFIG_GENWQE is not set ++# CONFIG_ECHO is not set ++# CONFIG_MISC_ALCOR_PCI is not set ++# CONFIG_MISC_RTSX_PCI is not set ++# CONFIG_MISC_RTSX_USB is not set ++# CONFIG_HABANA_AI is not set ++# CONFIG_UACCE is not set + CONFIG_MODS=m +-CONFIG_SENSORS_F75308=m ++# CONFIG_SAF775x_HWDEP is not set + CONFIG_SENSORS_NCT1008=m + CONFIG_SENSORS_PEX9749=y ++# CONFIG_TEGRA_CPC is not set ++# CONFIG_THERM_EST is not set + CONFIG_FAN_THERM_EST=y + CONFIG_EQOS_APE_HWDEP=m + CONFIG_TEGRA_ACSL=m ++# CONFIG_TEGRA_SKIN is not set + CONFIG_TEGRA_PCIE_EP_MEM=y ++# CONFIG_TEGRA_PCIE_DMA_TEST is not set ++# CONFIG_NVS is not set + CONFIG_NVS_LIGHT=m + CONFIG_NVS_PROXIMITY=y ++# CONFIG_NVS_TRIGGERED_BUFFER is not set + CONFIG_NVS_GTE=m + CONFIG_TEGRA_PROFILER=y ++CONFIG_EVENTLIB=y + CONFIG_NVSCIC2C_PCIE=m + CONFIG_NVSCIIPC=y + CONFIG_BLUEDROID_PM=m ++# end of Misc devices ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++CONFIG_RAID_ATTRS=m + CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# + CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++CONFIG_SCSI_SAS_ATTRS=m ++CONFIG_SCSI_SAS_LIBSAS=m + CONFIG_SCSI_SAS_ATA=y ++CONFIG_SCSI_SAS_HOST_SMP=y ++CONFIG_SCSI_SRP_ATTRS=m ++# end of SCSI Transports ++ ++CONFIG_SCSI_LOWLEVEL=y ++# CONFIG_ISCSI_TCP is not set ++# CONFIG_ISCSI_BOOT_SYSFS is not set ++# CONFIG_SCSI_CXGB3_ISCSI is not set ++# CONFIG_SCSI_CXGB4_ISCSI is not set ++# CONFIG_SCSI_BNX2_ISCSI is not set ++# CONFIG_BE2ISCSI is not set ++# CONFIG_BLK_DEV_3W_XXXX_RAID is not set ++# CONFIG_SCSI_HPSA is not set ++# CONFIG_SCSI_3W_9XXX is not set ++# CONFIG_SCSI_3W_SAS is not set ++# CONFIG_SCSI_ACARD is not set ++# CONFIG_SCSI_AACRAID is not set ++# CONFIG_SCSI_AIC7XXX is not set ++# CONFIG_SCSI_AIC79XX is not set ++# CONFIG_SCSI_AIC94XX is not set + CONFIG_SCSI_HISI_SAS=m + CONFIG_SCSI_HISI_SAS_PCI=m ++# CONFIG_SCSI_MVSAS is not set ++# CONFIG_SCSI_MVUMI is not set ++# CONFIG_SCSI_ADVANSYS is not set ++# CONFIG_SCSI_ARCMSR is not set ++# CONFIG_SCSI_ESAS2R is not set ++# CONFIG_MEGARAID_NEWGEN is not set ++# CONFIG_MEGARAID_LEGACY is not set ++# CONFIG_MEGARAID_SAS is not set + CONFIG_SCSI_MPT3SAS=m ++CONFIG_SCSI_MPT2SAS_MAX_SGE=128 ++CONFIG_SCSI_MPT3SAS_MAX_SGE=128 ++# CONFIG_SCSI_MPT2SAS is not set ++# CONFIG_SCSI_SMARTPQI is not set + CONFIG_SCSI_UFSHCD=y ++# CONFIG_SCSI_UFSHCD_PCI is not set + CONFIG_SCSI_UFSHCD_PLATFORM=y ++# CONFIG_SCSI_UFS_CDNS_PLATFORM is not set ++# CONFIG_SCSI_UFS_DWC_TC_PLATFORM is not set ++# CONFIG_SCSI_UFS_BSG is not set + CONFIG_SCSI_UFSHCD_TEGRA=y ++# CONFIG_SCSI_HPTIOP is not set ++# CONFIG_SCSI_MYRB is not set ++# CONFIG_SCSI_MYRS is not set ++# CONFIG_SCSI_SNIC is not set ++# CONFIG_SCSI_DMX3191D is not set ++# CONFIG_SCSI_FDOMAIN_PCI is not set ++# CONFIG_SCSI_GDTH is not set ++# CONFIG_SCSI_IPS is not set ++# CONFIG_SCSI_INITIO is not set ++# CONFIG_SCSI_INIA100 is not set ++# CONFIG_SCSI_STEX is not set ++# CONFIG_SCSI_SYM53C8XX_2 is not set ++# CONFIG_SCSI_IPR is not set ++# CONFIG_SCSI_QLOGIC_1280 is not set ++# CONFIG_SCSI_QLA_ISCSI is not set ++# CONFIG_SCSI_DC395x is not set ++# CONFIG_SCSI_AM53C974 is not set ++# CONFIG_SCSI_WD719X is not set ++# CONFIG_SCSI_DEBUG is not set ++# CONFIG_SCSI_PMCRAID is not set ++# CONFIG_SCSI_PM8001 is not set ++# CONFIG_SCSI_VIRTIO is not set ++# CONFIG_SCSI_DH is not set ++# end of SCSI device support ++ ++CONFIG_HAVE_PATA_PLATFORM=y + CONFIG_ATA=m ++CONFIG_SATA_HOST=y ++CONFIG_ATA_VERBOSE_ERROR=y ++CONFIG_ATA_FORCE=y + # CONFIG_ATA_ACPI is not set ++CONFIG_SATA_PMP=y ++ ++# ++# Controllers with non-SFF native interface ++# + CONFIG_SATA_AHCI=m ++CONFIG_SATA_MOBILE_LPM_POLICY=0 + CONFIG_SATA_AHCI_PLATFORM=m ++# CONFIG_AHCI_CEVA is not set ++# CONFIG_AHCI_TEGRA is not set ++# CONFIG_AHCI_QORIQ is not set ++# CONFIG_SATA_INIC162X is not set ++# CONFIG_SATA_ACARD_AHCI is not set ++# CONFIG_SATA_SIL24 is not set ++CONFIG_ATA_SFF=y ++ ++# ++# SFF controllers with custom DMA interface ++# ++# CONFIG_PDC_ADMA is not set ++# CONFIG_SATA_QSTOR is not set ++# CONFIG_SATA_SX4 is not set ++CONFIG_ATA_BMDMA=y ++ ++# ++# SATA SFF controllers with BMDMA ++# ++# CONFIG_ATA_PIIX is not set ++# CONFIG_SATA_DWC is not set ++# CONFIG_SATA_MV is not set ++# CONFIG_SATA_NV is not set ++# CONFIG_SATA_PROMISE is not set ++# CONFIG_SATA_SIL is not set ++# CONFIG_SATA_SIS is not set ++# CONFIG_SATA_SVW is not set ++# CONFIG_SATA_ULI is not set ++# CONFIG_SATA_VIA is not set ++# CONFIG_SATA_VITESSE is not set ++ ++# ++# PATA SFF controllers with BMDMA ++# ++# CONFIG_PATA_ALI is not set ++# CONFIG_PATA_AMD is not set ++# CONFIG_PATA_ARTOP is not set ++# CONFIG_PATA_ATIIXP is not set ++# CONFIG_PATA_ATP867X is not set ++# CONFIG_PATA_CMD64X is not set ++# CONFIG_PATA_CYPRESS is not set ++# CONFIG_PATA_EFAR is not set ++# CONFIG_PATA_HPT366 is not set ++# CONFIG_PATA_HPT37X is not set ++# CONFIG_PATA_HPT3X2N is not set ++# CONFIG_PATA_HPT3X3 is not set ++# CONFIG_PATA_IT8213 is not set ++# CONFIG_PATA_IT821X is not set ++# CONFIG_PATA_JMICRON is not set ++# CONFIG_PATA_MARVELL is not set ++# CONFIG_PATA_NETCELL is not set ++# CONFIG_PATA_NINJA32 is not set ++# CONFIG_PATA_NS87415 is not set ++# CONFIG_PATA_OLDPIIX is not set ++# CONFIG_PATA_OPTIDMA is not set ++# CONFIG_PATA_PDC2027X is not set ++# CONFIG_PATA_PDC_OLD is not set ++# CONFIG_PATA_RADISYS is not set ++# CONFIG_PATA_RDC is not set ++# CONFIG_PATA_SCH is not set ++# CONFIG_PATA_SERVERWORKS is not set ++# CONFIG_PATA_SIL680 is not set ++# CONFIG_PATA_SIS is not set ++# CONFIG_PATA_TOSHIBA is not set ++# CONFIG_PATA_TRIFLEX is not set ++# CONFIG_PATA_VIA is not set ++# CONFIG_PATA_WINBOND is not set ++ ++# ++# PIO-only SFF controllers ++# ++# CONFIG_PATA_CMD640_PCI is not set ++# CONFIG_PATA_MPIIX is not set ++# CONFIG_PATA_NS87410 is not set ++# CONFIG_PATA_OPTI is not set ++# CONFIG_PATA_PLATFORM is not set ++# CONFIG_PATA_RZ1000 is not set ++ ++# ++# Generic fallback / legacy drivers ++# ++# CONFIG_ATA_GENERIC is not set ++# CONFIG_PATA_LEGACY is not set ++# CONFIG_AHCI_TEGRA_DOWNSTREAM is not set ++# CONFIG_SATA_AHCI_TEGRA_SHIELD is not set + CONFIG_MD=y ++# CONFIG_BLK_DEV_MD is not set ++# CONFIG_BCACHE is not set ++CONFIG_BLK_DEV_DM_BUILTIN=y + CONFIG_BLK_DEV_DM=y ++# CONFIG_DM_DEBUG is not set ++CONFIG_DM_BUFIO=y ++# CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING is not set ++# CONFIG_DM_UNSTRIPED is not set + CONFIG_DM_CRYPT=y ++# CONFIG_DM_SNAPSHOT is not set ++# CONFIG_DM_THIN_PROVISIONING is not set ++# CONFIG_DM_CACHE is not set ++# CONFIG_DM_WRITECACHE is not set ++# CONFIG_DM_EBS is not set ++# CONFIG_DM_ERA is not set ++# CONFIG_DM_CLONE is not set ++# CONFIG_DM_MIRROR is not set ++# CONFIG_DM_RAID is not set ++# CONFIG_DM_ZERO is not set ++# CONFIG_DM_MULTIPATH is not set ++# CONFIG_DM_DELAY is not set ++# CONFIG_DM_DUST is not set ++# CONFIG_DM_INIT is not set + CONFIG_DM_UEVENT=y ++# CONFIG_DM_FLAKEY is not set + CONFIG_DM_VERITY=y + CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y ++# CONFIG_DM_VERITY_FEC is not set ++# CONFIG_DM_SWITCH is not set ++# CONFIG_DM_LOG_WRITES is not set ++# CONFIG_DM_INTEGRITY is not set ++# CONFIG_TARGET_CORE is not set ++# CONFIG_FUSION is not set ++ ++# ++# IEEE 1394 (FireWire) support ++# ++# CONFIG_FIREWIRE is not set ++# CONFIG_FIREWIRE_NOSY is not set ++# end of IEEE 1394 (FireWire) support ++ + CONFIG_NETDEVICES=y ++CONFIG_MII=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set + CONFIG_DUMMY=y ++# CONFIG_WIREGUARD is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_FC is not set ++# CONFIG_IFB is not set ++# CONFIG_NET_TEAM is not set + CONFIG_MACVLAN=m + CONFIG_MACVTAP=m ++CONFIG_IPVLAN_L3S=y + CONFIG_IPVLAN=m ++# CONFIG_IPVTAP is not set + CONFIG_VXLAN=y ++# CONFIG_GENEVE is not set ++# CONFIG_BAREUDP is not set ++# CONFIG_GTP is not set ++# CONFIG_MACSEC is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NTB_NETDEV is not set + CONFIG_TUN=y ++CONFIG_TAP=m ++# CONFIG_TUN_VNET_CROSS_LE is not set + CONFIG_VETH=m + CONFIG_VIRTIO_NET=y ++# CONFIG_NLMON is not set ++# CONFIG_NET_VRF is not set ++# CONFIG_ARCNET is not set ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_B53 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++# CONFIG_NET_DSA_LOOP is not set ++# CONFIG_NET_DSA_LANTIQ_GSWIP is not set ++# CONFIG_NET_DSA_MT7530 is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MICROCHIP_KSZ9477 is not set ++# CONFIG_NET_DSA_MICROCHIP_KSZ8795 is not set ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MSCC_SEVILLE is not set ++# CONFIG_NET_DSA_AR9331 is not set ++# CONFIG_NET_DSA_SJA1105 is not set ++# CONFIG_NET_DSA_QCA8K is not set ++# CONFIG_NET_DSA_REALTEK_SMI is not set ++# CONFIG_NET_DSA_SMSC_LAN9303_I2C is not set ++# CONFIG_NET_DSA_SMSC_LAN9303_MDIO is not set ++# CONFIG_NET_DSA_VITESSE_VSC73XX_SPI is not set ++# CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM is not set ++# end of Distributed Switch Architecture drivers ++ ++CONFIG_ETHERNET=y ++CONFIG_MDIO=m ++CONFIG_NET_VENDOR_3COM=y ++# CONFIG_VORTEX is not set + CONFIG_TYPHOON=m ++CONFIG_NET_VENDOR_ADAPTEC=y ++# CONFIG_ADAPTEC_STARFIRE is not set ++CONFIG_NET_VENDOR_AGERE=y + CONFIG_ET131X=m ++CONFIG_NET_VENDOR_ALACRITECH=y + CONFIG_SLICOSS=m ++CONFIG_NET_VENDOR_ALTEON=y + CONFIG_ACENIC=m ++# CONFIG_ACENIC_OMIT_TIGON_I is not set + CONFIG_ALTERA_TSE=m ++CONFIG_NET_VENDOR_AMAZON=y ++# CONFIG_ENA_ETHERNET is not set ++CONFIG_NET_VENDOR_AMD=y ++# CONFIG_AMD8111_ETH is not set ++# CONFIG_PCNET32 is not set ++# CONFIG_AMD_XGBE is not set ++CONFIG_NET_VENDOR_AQUANTIA=y + CONFIG_AQTION=m ++CONFIG_NET_VENDOR_ARC=y ++CONFIG_NET_VENDOR_ATHEROS=y + CONFIG_ATL2=m + CONFIG_ATL1=m + CONFIG_ATL1E=m + CONFIG_ATL1C=m + CONFIG_ALX=m + # CONFIG_NET_VENDOR_AURORA is not set ++CONFIG_NET_VENDOR_BROADCOM=y + CONFIG_B44=m ++CONFIG_B44_PCI_AUTOSELECT=y ++CONFIG_B44_PCICORE_AUTOSELECT=y ++CONFIG_B44_PCI=y ++# CONFIG_BCMGENET is not set ++CONFIG_BNX2=m + CONFIG_CNIC=m + CONFIG_TIGON3=y ++CONFIG_TIGON3_HWMON=y + CONFIG_BNX2X=m ++CONFIG_BNX2X_SRIOV=y ++# CONFIG_SYSTEMPORT is not set + CONFIG_BNXT=m ++CONFIG_BNXT_SRIOV=y ++CONFIG_BNXT_FLOWER_OFFLOAD=y ++CONFIG_BNXT_HWMON=y ++CONFIG_NET_VENDOR_BROCADE=y + CONFIG_BNA=m ++CONFIG_NET_VENDOR_CADENCE=y + CONFIG_MACB=y ++CONFIG_MACB_USE_HWSTAMP=y ++# CONFIG_MACB_PCI is not set ++CONFIG_NET_VENDOR_CAVIUM=y + CONFIG_THUNDER_NIC_PF=m + CONFIG_THUNDER_NIC_VF=m ++CONFIG_THUNDER_NIC_BGX=m ++CONFIG_THUNDER_NIC_RGX=m + # CONFIG_CAVIUM_PTP is not set + CONFIG_LIQUIDIO=m + CONFIG_LIQUIDIO_VF=m ++CONFIG_NET_VENDOR_CHELSIO=y + CONFIG_CHELSIO_T1=m + CONFIG_CHELSIO_T1_1G=y + CONFIG_CHELSIO_T3=m + CONFIG_CHELSIO_T4=m + CONFIG_CHELSIO_T4VF=m ++CONFIG_CHELSIO_INLINE_CRYPTO=y ++CONFIG_NET_VENDOR_CISCO=y + CONFIG_ENIC=m ++CONFIG_NET_VENDOR_CORTINA=y ++# CONFIG_GEMINI_ETHERNET is not set ++# CONFIG_DNET is not set ++CONFIG_NET_VENDOR_DEC=y ++# CONFIG_NET_TULIP is not set ++CONFIG_NET_VENDOR_DLINK=y + CONFIG_DL2K=m ++# CONFIG_SUNDANCE is not set ++CONFIG_NET_VENDOR_EMULEX=y + CONFIG_BE2NET=m ++CONFIG_BE2NET_HWMON=y ++CONFIG_BE2NET_BE2=y ++CONFIG_BE2NET_BE3=y ++CONFIG_BE2NET_LANCER=y ++CONFIG_BE2NET_SKYHAWK=y ++CONFIG_NET_VENDOR_EZCHIP=y ++# CONFIG_EZCHIP_NPS_MANAGEMENT_ENET is not set ++CONFIG_NET_VENDOR_GOOGLE=y ++# CONFIG_GVE is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++# CONFIG_HIP04_ETH is not set ++CONFIG_HNS_MDIO=y ++CONFIG_HNS=y + CONFIG_HNS_DSAF=y + CONFIG_HNS_ENET=y + CONFIG_HNS3=y + # CONFIG_HNS3_HCLGE is not set + CONFIG_HNS3_ENET=y ++CONFIG_NET_VENDOR_HUAWEI=y + CONFIG_HINIC=m ++CONFIG_NET_VENDOR_I825XX=y ++CONFIG_NET_VENDOR_INTEL=y + CONFIG_E100=m + CONFIG_E1000=m + CONFIG_E1000E=y + CONFIG_IGB=y ++CONFIG_IGB_HWMON=y + CONFIG_IGBVF=m + CONFIG_IXGB=m + CONFIG_IXGBE=m + # CONFIG_IXGBE_HWMON is not set + CONFIG_IXGBEVF=m + CONFIG_I40E=m ++CONFIG_IAVF=m + CONFIG_I40EVF=m + CONFIG_ICE=m + CONFIG_FM10K=m + CONFIG_IGC=m + CONFIG_JME=m ++CONFIG_NET_VENDOR_MARVELL=y ++# CONFIG_MVMDIO is not set + CONFIG_SKGE=m ++# CONFIG_SKGE_DEBUG is not set ++# CONFIG_SKGE_GENESIS is not set + CONFIG_SKY2=m + CONFIG_SKY2_DEBUG=y + CONFIG_OAK=m ++# CONFIG_OCTEONTX2_AF is not set ++# CONFIG_OCTEONTX2_PF is not set ++# CONFIG_PRESTERA is not set ++CONFIG_NET_VENDOR_MELLANOX=y + CONFIG_MLX4_EN=m ++CONFIG_MLX4_CORE=m ++CONFIG_MLX4_DEBUG=y ++CONFIG_MLX4_CORE_GEN2=y + CONFIG_MLX5_CORE=m ++# CONFIG_MLX5_FPGA is not set + CONFIG_MLX5_CORE_EN=y ++CONFIG_MLX5_EN_ARFS=y ++CONFIG_MLX5_EN_RXNFC=y ++CONFIG_MLX5_MPFS=y ++CONFIG_MLX5_ESWITCH=y ++CONFIG_MLX5_CLS_ACT=y + CONFIG_MLX5_CORE_IPOIB=y ++CONFIG_MLX5_SW_STEERING=y + CONFIG_MLX5_CORE_THERMAL=y + CONFIG_MLXSW_CORE=y ++CONFIG_MLXSW_CORE_HWMON=y ++CONFIG_MLXSW_CORE_THERMAL=y ++CONFIG_MLXSW_PCI=m ++CONFIG_MLXSW_I2C=m ++CONFIG_MLXSW_SWITCHIB=m ++CONFIG_MLXSW_SWITCHX2=m ++CONFIG_MLXSW_SPECTRUM=m ++CONFIG_MLXSW_MINIMAL=m ++CONFIG_MLXFW=y + CONFIG_MLX_MFT=m ++CONFIG_NET_VENDOR_MICREL=y ++# CONFIG_KS8842 is not set ++# CONFIG_KS8851 is not set ++# CONFIG_KS8851_MLL is not set ++# CONFIG_KSZ884X_PCI is not set ++CONFIG_NET_VENDOR_MICROCHIP=y ++# CONFIG_ENC28J60 is not set ++# CONFIG_ENCX24J600 is not set + CONFIG_LAN743X=m ++CONFIG_NET_VENDOR_MICROSEMI=y ++# CONFIG_MSCC_OCELOT_SWITCH is not set ++CONFIG_NET_VENDOR_MYRI=y ++# CONFIG_MYRI10GE is not set ++# CONFIG_FEALNX is not set ++CONFIG_NET_VENDOR_NATSEMI=y + CONFIG_NATSEMI=m + CONFIG_NS83820=m ++CONFIG_NET_VENDOR_NETERION=y + CONFIG_S2IO=m + CONFIG_VXGE=m ++# CONFIG_VXGE_DEBUG_TRACE_ALL is not set ++CONFIG_NET_VENDOR_NETRONOME=y + CONFIG_NFP=m ++CONFIG_NFP_APP_FLOWER=y ++CONFIG_NFP_APP_ABM_NIC=y + CONFIG_NFP_DEBUG=y ++CONFIG_NET_VENDOR_NI=y + CONFIG_NI_XGE_MANAGEMENT_ENET=m ++CONFIG_NET_VENDOR_8390=y + CONFIG_NE2K_PCI=m ++CONFIG_NET_VENDOR_NVIDIA=y + CONFIG_FORCEDETH=y + CONFIG_PCIE_TEGRA_VNET=y ++CONFIG_NET_VENDOR_OKI=y ++# CONFIG_ETHOC is not set ++CONFIG_NET_VENDOR_PACKET_ENGINES=y + CONFIG_HAMACHI=m + CONFIG_YELLOWFIN=m ++CONFIG_NET_VENDOR_PENSANDO=y + CONFIG_IONIC=m ++CONFIG_NET_VENDOR_QLOGIC=y + CONFIG_QLA3XXX=m + CONFIG_QLCNIC=m ++CONFIG_QLCNIC_SRIOV=y ++CONFIG_QLCNIC_HWMON=y + CONFIG_NETXEN_NIC=m ++# CONFIG_QED is not set ++CONFIG_NET_VENDOR_QUALCOMM=y ++CONFIG_QCA7000=m + CONFIG_QCA7000_SPI=m ++# CONFIG_QCA7000_UART is not set + CONFIG_QCOM_EMAC=m ++# CONFIG_RMNET is not set ++CONFIG_NET_VENDOR_RDC=y ++# CONFIG_R6040 is not set ++CONFIG_NET_VENDOR_REALTEK=y + CONFIG_8139CP=m + CONFIG_8139TOO=m ++CONFIG_8139TOO_PIO=y ++# CONFIG_8139TOO_TUNE_TWISTER is not set ++# CONFIG_8139TOO_8129 is not set ++# CONFIG_8139_OLD_RX_RESET is not set + CONFIG_R8169=m + CONFIG_R8168=m ++CONFIG_R8168_NAPI=y ++CONFIG_R8168_VLAN=y ++CONFIG_R8168_ASPM=y ++CONFIG_R8168_S5WOL=y ++CONFIG_NET_VENDOR_RENESAS=y ++CONFIG_NET_VENDOR_ROCKER=y ++# CONFIG_ROCKER is not set ++CONFIG_NET_VENDOR_SAMSUNG=y + CONFIG_SXGBE_ETH=m ++CONFIG_NET_VENDOR_SEEQ=y ++CONFIG_NET_VENDOR_SOLARFLARE=y + CONFIG_SFC=m ++CONFIG_SFC_MTD=y ++CONFIG_SFC_MCDI_MON=y ++CONFIG_SFC_SRIOV=y ++CONFIG_SFC_MCDI_LOGGING=y ++# CONFIG_SFC_FALCON is not set ++CONFIG_NET_VENDOR_SILAN=y ++# CONFIG_SC92031 is not set ++CONFIG_NET_VENDOR_SIS=y ++# CONFIG_SIS900 is not set ++# CONFIG_SIS190 is not set ++CONFIG_NET_VENDOR_SMSC=y + CONFIG_SMC91X=y ++# CONFIG_EPIC100 is not set + CONFIG_SMSC911X=y ++# CONFIG_SMSC9420 is not set + # CONFIG_NET_VENDOR_SOCIONEXT is not set ++CONFIG_NET_VENDOR_STMICRO=y + CONFIG_STMMAC_ETH=m ++# CONFIG_STMMAC_SELFTESTS is not set ++CONFIG_STMMAC_PLATFORM=m ++# CONFIG_DWMAC_DWC_QOS_ETH is not set ++CONFIG_DWMAC_GENERIC=m ++# CONFIG_DWMAC_INTEL_PLAT is not set ++# CONFIG_STMMAC_PCI is not set ++CONFIG_NET_VENDOR_SUN=y ++# CONFIG_HAPPYMEAL is not set ++# CONFIG_SUNGEM is not set ++# CONFIG_CASSINI is not set ++# CONFIG_NIU is not set ++CONFIG_NET_VENDOR_SYNOPSYS=y ++# CONFIG_DWC_XLGMAC is not set ++CONFIG_NET_VENDOR_TEHUTI=y ++# CONFIG_TEHUTI is not set ++CONFIG_NET_VENDOR_TI=y ++# CONFIG_TI_CPSW_PHY_SEL is not set ++# CONFIG_TLAN is not set ++CONFIG_NET_VENDOR_VIA=y ++# CONFIG_VIA_RHINE is not set ++# CONFIG_VIA_VELOCITY is not set ++CONFIG_NET_VENDOR_WIZNET=y ++# CONFIG_WIZNET_W5100 is not set ++# CONFIG_WIZNET_W5300 is not set + # CONFIG_NET_VENDOR_XILINX is not set + CONFIG_NVETHERNET=y + CONFIG_NVETHERNET_SELFTESTS=y ++# CONFIG_FDDI is not set ++# CONFIG_HIPPI is not set ++# CONFIG_NET_SB1000 is not set ++CONFIG_PHYLINK=y ++CONFIG_PHYLIB=y ++CONFIG_SWPHY=y ++# CONFIG_LED_TRIGGER_PHY is not set ++CONFIG_FIXED_PHY=y ++# CONFIG_SFP is not set ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AMD_PHY is not set ++# CONFIG_ADIN_PHY is not set + CONFIG_AQUANTIA_PHY=y ++# CONFIG_AX88796B_PHY is not set + CONFIG_BROADCOM_PHY=y ++# CONFIG_BCM54140_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM84881_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++CONFIG_BCM_NET_PHYLIB=y ++# CONFIG_CICADA_PHY is not set ++# CONFIG_CORTINA_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_INTEL_XWAY_PHY is not set ++# CONFIG_LSI_ET1011C_PHY is not set + CONFIG_MARVELL_PHY=y + CONFIG_MARVELL_10G_PHY=m + CONFIG_MICREL_PHY=m ++CONFIG_MICROCHIP_PHY=m ++# CONFIG_MICROCHIP_T1_PHY is not set ++# CONFIG_MICROSEMI_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_NXP_TJA11XX_PHY is not set ++# CONFIG_AT803X_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++CONFIG_REALTEK_PHY=m ++# CONFIG_RENESAS_PHY is not set ++# CONFIG_ROCKCHIP_PHY is not set ++CONFIG_SMSC_PHY=m ++# CONFIG_STE10XP is not set ++# CONFIG_TERANETICS_PHY is not set ++# CONFIG_DP83822_PHY is not set ++# CONFIG_DP83TC811_PHY is not set ++# CONFIG_DP83848_PHY is not set ++# CONFIG_DP83867_PHY is not set ++# CONFIG_DP83869_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_XILINX_GMII2RGMII is not set ++# CONFIG_MICREL_KS8995MA is not set ++CONFIG_MDIO_DEVICE=y ++CONFIG_MDIO_BUS=y ++CONFIG_OF_MDIO=y ++CONFIG_MDIO_DEVRES=y + CONFIG_MDIO_BITBANG=y ++# CONFIG_MDIO_BCM_UNIMAC is not set ++CONFIG_MDIO_CAVIUM=m ++# CONFIG_MDIO_GPIO is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++# CONFIG_MDIO_MVUSB is not set ++# CONFIG_MDIO_MSCC_MIIM is not set ++# CONFIG_MDIO_OCTEON is not set ++# CONFIG_MDIO_IPQ4019 is not set ++# CONFIG_MDIO_IPQ8064 is not set ++CONFIG_MDIO_THUNDER=m ++ ++# ++# MDIO Multiplexers ++# ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MULTIPLEXER is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++ ++# ++# PCS device drivers ++# ++CONFIG_PCS_XPCS=m ++# end of PCS device drivers ++ + CONFIG_PPP=y + CONFIG_PPP_BSDCOMP=y + CONFIG_PPP_DEFLATE=y + CONFIG_PPP_FILTER=y + CONFIG_PPP_MPPE=y ++# CONFIG_PPP_MULTILINK is not set ++# CONFIG_PPPOE is not set ++# CONFIG_PPTP is not set + CONFIG_PPP_ASYNC=y + CONFIG_PPP_SYNC_TTY=y ++# CONFIG_SLIP is not set ++CONFIG_SLHC=y ++CONFIG_USB_NET_DRIVERS=y + CONFIG_USB_CATC=m + CONFIG_USB_KAWETH=m + CONFIG_USB_PEGASUS=m +@@ -516,7 +2831,12 @@ CONFIG_USB_RTL8150=m + CONFIG_USB_RTL8152=y + CONFIG_USB_LAN78XX=m + CONFIG_USB_USBNET=y ++CONFIG_USB_NET_AX8817X=y ++CONFIG_USB_NET_AX88179_178A=y ++CONFIG_USB_NET_CDCETHER=y + CONFIG_USB_NET_CDC_EEM=m ++CONFIG_USB_NET_CDC_NCM=y ++# CONFIG_USB_NET_HUAWEI_CDC_NCM is not set + CONFIG_USB_NET_CDC_MBIM=m + CONFIG_USB_NET_DM9601=m + CONFIG_USB_NET_SR9700=m +@@ -524,90 +2844,259 @@ CONFIG_USB_NET_SR9800=m + CONFIG_USB_NET_SMSC75XX=m + CONFIG_USB_NET_SMSC95XX=m + CONFIG_USB_NET_GL620A=m ++CONFIG_USB_NET_NET1080=y + CONFIG_USB_NET_PLUSB=m + CONFIG_USB_NET_MCS7830=m + CONFIG_USB_NET_RNDIS_HOST=m ++CONFIG_USB_NET_CDC_SUBSET_ENABLE=y ++CONFIG_USB_NET_CDC_SUBSET=y + CONFIG_USB_ALI_M5632=y + CONFIG_USB_AN2720=y ++CONFIG_USB_BELKIN=y ++CONFIG_USB_ARMLINUX=y ++# CONFIG_USB_EPSON2888 is not set + CONFIG_USB_KC2190=y ++CONFIG_USB_NET_ZAURUS=y + CONFIG_USB_NET_CX82310_ETH=m ++# CONFIG_USB_NET_KALMIA is not set ++# CONFIG_USB_NET_QMI_WWAN is not set ++# CONFIG_USB_HSO is not set ++# CONFIG_USB_NET_INT51X1 is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_USB_SIERRA_NET is not set ++# CONFIG_USB_VL600 is not set ++# CONFIG_USB_NET_CH9200 is not set + CONFIG_USB_NET_AQC111=y ++# CONFIG_USB_RTL8152_SHIELD is not set ++CONFIG_WLAN=y ++# CONFIG_WIRELESS_WDS is not set ++CONFIG_WLAN_VENDOR_ADMTEK=y ++# CONFIG_ADM8211 is not set ++CONFIG_ATH_COMMON=m ++CONFIG_WLAN_VENDOR_ATH=y ++# CONFIG_ATH_DEBUG is not set ++# CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS is not set ++# CONFIG_ATH5K is not set ++# CONFIG_ATH5K_PCI is not set ++CONFIG_ATH9K_HW=m ++CONFIG_ATH9K_COMMON=m ++CONFIG_ATH9K_COMMON_DEBUG=y ++CONFIG_ATH9K_BTCOEX_SUPPORT=y + CONFIG_ATH9K=m ++CONFIG_ATH9K_PCI=y ++# CONFIG_ATH9K_AHB is not set + CONFIG_ATH9K_DEBUGFS=y + CONFIG_ATH9K_STATION_STATISTICS=y ++# CONFIG_ATH9K_TX99 is not set ++# CONFIG_ATH9K_DFS_CERTIFIED is not set ++# CONFIG_ATH9K_DYNACK is not set + CONFIG_ATH9K_WOW=y ++CONFIG_ATH9K_RFKILL=y ++# CONFIG_ATH9K_CHANNEL_CONTEXT is not set ++CONFIG_ATH9K_PCOEM=y ++# CONFIG_ATH9K_PCI_NO_EEPROM is not set + CONFIG_ATH9K_HTC=m + CONFIG_ATH9K_HTC_DEBUGFS=y ++# CONFIG_ATH9K_HWRNG is not set ++# CONFIG_ATH9K_COMMON_SPECTRAL is not set + CONFIG_CARL9170=m ++CONFIG_CARL9170_LEDS=y + CONFIG_CARL9170_DEBUGFS=y ++CONFIG_CARL9170_WPC=y ++# CONFIG_CARL9170_HWRNG is not set + CONFIG_ATH6KL=m + CONFIG_ATH6KL_SDIO=m + CONFIG_ATH6KL_USB=m ++# CONFIG_ATH6KL_DEBUG is not set ++# CONFIG_ATH6KL_TRACING is not set ++# CONFIG_ATH6KL_REGDOMAIN is not set + CONFIG_AR5523=m + CONFIG_WIL6210=m ++CONFIG_WIL6210_ISR_COR=y ++# CONFIG_WIL6210_TRACING is not set ++CONFIG_WIL6210_DEBUGFS=y + CONFIG_ATH10K=m ++CONFIG_ATH10K_CE=y + CONFIG_ATH10K_PCI=m + CONFIG_ATH10K_AHB=y + CONFIG_ATH10K_SDIO=m + CONFIG_ATH10K_USB=m ++# CONFIG_ATH10K_DEBUG is not set + CONFIG_ATH10K_DEBUGFS=y ++# CONFIG_ATH10K_SPECTRAL is not set ++# CONFIG_ATH10K_TRACING is not set ++# CONFIG_ATH10K_DFS_CERTIFIED is not set + CONFIG_WCN36XX=m + CONFIG_WCN36XX_DEBUGFS=y ++# CONFIG_ATH11K is not set ++CONFIG_WLAN_VENDOR_ATMEL=y + CONFIG_ATMEL=m ++# CONFIG_PCI_ATMEL is not set + CONFIG_AT76C50X_USB=m ++CONFIG_WLAN_VENDOR_BROADCOM=y + CONFIG_B43=m ++CONFIG_B43_BCMA=y ++CONFIG_B43_SSB=y ++CONFIG_B43_BUSES_BCMA_AND_SSB=y ++# CONFIG_B43_BUSES_BCMA is not set ++# CONFIG_B43_BUSES_SSB is not set ++CONFIG_B43_PCI_AUTOSELECT=y ++CONFIG_B43_PCICORE_AUTOSELECT=y + CONFIG_B43_SDIO=y ++CONFIG_B43_BCMA_PIO=y ++CONFIG_B43_PIO=y ++CONFIG_B43_PHY_G=y ++CONFIG_B43_PHY_N=y ++CONFIG_B43_PHY_LP=y ++CONFIG_B43_PHY_HT=y ++CONFIG_B43_LEDS=y ++CONFIG_B43_HWRNG=y ++# CONFIG_B43_DEBUG is not set + CONFIG_B43LEGACY=m ++CONFIG_B43LEGACY_PCI_AUTOSELECT=y ++CONFIG_B43LEGACY_PCICORE_AUTOSELECT=y ++CONFIG_B43LEGACY_LEDS=y ++CONFIG_B43LEGACY_HWRNG=y ++CONFIG_B43LEGACY_DEBUG=y ++CONFIG_B43LEGACY_DMA=y ++CONFIG_B43LEGACY_PIO=y ++CONFIG_B43LEGACY_DMA_AND_PIO_MODE=y ++# CONFIG_B43LEGACY_DMA_MODE is not set ++# CONFIG_B43LEGACY_PIO_MODE is not set ++CONFIG_BRCMUTIL=m ++# CONFIG_BRCMSMAC is not set + CONFIG_BRCMFMAC=m ++CONFIG_BRCMFMAC_PROTO_MSGBUF=y + # CONFIG_BRCMFMAC_SDIO is not set ++# CONFIG_BRCMFMAC_USB is not set + CONFIG_BRCMFMAC_PCIE=y ++# CONFIG_BRCM_TRACING is not set ++# CONFIG_BRCMDBG is not set ++CONFIG_WLAN_VENDOR_CISCO=y ++CONFIG_WLAN_VENDOR_INTEL=y + CONFIG_IPW2100=m ++# CONFIG_IPW2100_MONITOR is not set ++# CONFIG_IPW2100_DEBUG is not set + CONFIG_IPW2200=m ++# CONFIG_IPW2200_MONITOR is not set ++# CONFIG_IPW2200_QOS is not set ++# CONFIG_IPW2200_DEBUG is not set ++CONFIG_LIBIPW=m ++# CONFIG_LIBIPW_DEBUG is not set ++CONFIG_IWLEGACY=m ++# CONFIG_IWL4965 is not set + CONFIG_IWL3945=m ++ ++# ++# iwl3945 / iwl4965 Debugging Options ++# ++# CONFIG_IWLEGACY_DEBUG is not set ++# CONFIG_IWLEGACY_DEBUGFS is not set ++# end of iwl3945 / iwl4965 Debugging Options ++ + CONFIG_IWLWIFI=m ++CONFIG_IWLWIFI_LEDS=y + CONFIG_IWLDVM=m + CONFIG_IWLMVM=m ++CONFIG_IWLWIFI_OPMODE_MODULAR=y + CONFIG_IWLWIFI_BCAST_FILTERING=y ++ ++# ++# Debugging Options ++# + CONFIG_IWLWIFI_DEBUG=y + CONFIG_IWLWIFI_DEBUGFS=y ++CONFIG_IWLWIFI_DEVICE_TRACING=y ++# end of Debugging Options ++ ++CONFIG_WLAN_VENDOR_INTERSIL=y ++# CONFIG_HOSTAP is not set + CONFIG_HERMES=m + CONFIG_HERMES_PRISM=y ++CONFIG_HERMES_CACHE_FW_ON_INIT=y + CONFIG_PLX_HERMES=m + CONFIG_TMD_HERMES=m + CONFIG_NORTEL_HERMES=m ++# CONFIG_PCI_HERMES is not set + CONFIG_ORINOCO_USB=m + CONFIG_P54_COMMON=m + CONFIG_P54_USB=m + CONFIG_P54_PCI=m + CONFIG_P54_SPI=m ++# CONFIG_P54_SPI_DEFAULT_EEPROM is not set ++CONFIG_P54_LEDS=y ++# CONFIG_PRISM54 is not set ++CONFIG_WLAN_VENDOR_MARVELL=y + CONFIG_LIBERTAS=m ++# CONFIG_LIBERTAS_USB is not set ++# CONFIG_LIBERTAS_SDIO is not set ++# CONFIG_LIBERTAS_SPI is not set ++# CONFIG_LIBERTAS_DEBUG is not set ++# CONFIG_LIBERTAS_MESH is not set + CONFIG_LIBERTAS_THINFIRM=m ++# CONFIG_LIBERTAS_THINFIRM_DEBUG is not set ++# CONFIG_LIBERTAS_THINFIRM_USB is not set + CONFIG_MWIFIEX=m + CONFIG_MWIFIEX_SDIO=m + CONFIG_MWIFIEX_PCIE=m + CONFIG_MWIFIEX_USB=m + CONFIG_MWL8K=m ++CONFIG_WLAN_VENDOR_MEDIATEK=y + CONFIG_MT7601U=m ++CONFIG_MT76_CORE=m ++CONFIG_MT76_LEDS=y ++CONFIG_MT76_USB=m ++CONFIG_MT76x02_LIB=m ++CONFIG_MT76x02_USB=m ++CONFIG_MT76x0_COMMON=m + CONFIG_MT76x0U=m + CONFIG_MT76x0E=m ++CONFIG_MT76x2_COMMON=m + CONFIG_MT76x2E=m + CONFIG_MT76x2U=m + CONFIG_MT7603E=m ++CONFIG_MT7615_COMMON=m + CONFIG_MT7615E=m ++CONFIG_MT7663_USB_SDIO_COMMON=m + CONFIG_MT7663U=m ++# CONFIG_MT7663S is not set + CONFIG_MT7915E=m + # CONFIG_WLAN_VENDOR_MICROCHIP is not set ++CONFIG_WLAN_VENDOR_RALINK=y + CONFIG_RT2X00=m ++# CONFIG_RT2400PCI is not set ++# CONFIG_RT2500PCI is not set ++# CONFIG_RT61PCI is not set + CONFIG_RT2800PCI=m ++CONFIG_RT2800PCI_RT33XX=y ++CONFIG_RT2800PCI_RT35XX=y ++CONFIG_RT2800PCI_RT53XX=y ++CONFIG_RT2800PCI_RT3290=y + CONFIG_RT2500USB=m + CONFIG_RT73USB=m + CONFIG_RT2800USB=m ++CONFIG_RT2800USB_RT33XX=y ++CONFIG_RT2800USB_RT35XX=y + CONFIG_RT2800USB_RT3573=y + CONFIG_RT2800USB_RT53XX=y + CONFIG_RT2800USB_RT55XX=y + CONFIG_RT2800USB_UNKNOWN=y ++CONFIG_RT2800_LIB=m ++CONFIG_RT2800_LIB_MMIO=m ++CONFIG_RT2X00_LIB_MMIO=m ++CONFIG_RT2X00_LIB_PCI=m ++CONFIG_RT2X00_LIB_USB=m ++CONFIG_RT2X00_LIB=m ++CONFIG_RT2X00_LIB_FIRMWARE=y ++CONFIG_RT2X00_LIB_CRYPTO=y ++CONFIG_RT2X00_LIB_LEDS=y + CONFIG_RT2X00_LIB_DEBUGFS=y ++# CONFIG_RT2X00_DEBUG is not set ++CONFIG_WLAN_VENDOR_REALTEK=y ++# CONFIG_RTL8180 is not set + CONFIG_RTL8187=m ++CONFIG_RTL8187_LEDS=y ++CONFIG_RTL_CARDS=m + CONFIG_RTL8192CE=m + CONFIG_RTL8192SE=m + CONFIG_RTL8192DE=m +@@ -617,36 +3106,120 @@ CONFIG_RTL8188EE=m + CONFIG_RTL8192EE=m + CONFIG_RTL8821AE=m + CONFIG_RTL8192CU=m ++CONFIG_RTLWIFI=m ++CONFIG_RTLWIFI_PCI=m ++CONFIG_RTLWIFI_USB=m ++CONFIG_RTLWIFI_DEBUG=y ++CONFIG_RTL8192C_COMMON=m ++CONFIG_RTL8723_COMMON=m ++CONFIG_RTLBTCOEXIST=m + CONFIG_RTL8XXXU=m + CONFIG_RTL8XXXU_UNTESTED=y ++# CONFIG_RTW88 is not set ++CONFIG_WLAN_VENDOR_RSI=y + CONFIG_RSI_91X=m ++CONFIG_RSI_DEBUGFS=y ++CONFIG_RSI_SDIO=m ++CONFIG_RSI_USB=m + # CONFIG_RSI_COEX is not set ++CONFIG_WLAN_VENDOR_ST=y + CONFIG_CW1200=m + CONFIG_CW1200_WLAN_SDIO=m ++# CONFIG_CW1200_WLAN_SPI is not set ++CONFIG_WLAN_VENDOR_TI=y + CONFIG_WL1251=m ++# CONFIG_WL1251_SPI is not set + CONFIG_WL1251_SDIO=m + CONFIG_WL12XX=m + CONFIG_WL18XX=m ++CONFIG_WLCORE=m ++# CONFIG_WLCORE_SPI is not set + CONFIG_WLCORE_SDIO=m ++CONFIG_WILINK_PLATFORM_DATA=y ++CONFIG_WLAN_VENDOR_ZYDAS=y + CONFIG_USB_ZD1201=m + CONFIG_ZD1211RW=m ++# CONFIG_ZD1211RW_DEBUG is not set ++CONFIG_WLAN_VENDOR_QUANTENNA=y ++# CONFIG_QTNFMAC_PCIE is not set ++# CONFIG_MAC80211_HWSIM is not set ++# CONFIG_USB_NET_RNDIS_WLAN is not set ++# CONFIG_VIRT_WIFI is not set + CONFIG_BCMDHD=m ++# CONFIG_BCMDHD_SDIO is not set ++# CONFIG_BCMDHD_PCIE is not set ++# CONFIG_BCMDYNAMIC is not set ++# CONFIG_BCM43241 is not set + CONFIG_BCM4354=y ++CONFIG_BCMDHD_FW_PATH="/system/vendor/firmware/fw_bcmdhd.bin" ++CONFIG_BCMDHD_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" + CONFIG_BCMDHD_HW_OOB=y ++# CONFIG_DHD_USE_STATIC_BUF is not set + CONFIG_DHD_USE_SCHED_SCAN=y + CONFIG_BCMDHD_DISABLE_MCC=y ++# CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA is not set ++# CONFIG_BCMDHD_CUSTOM_NET_PERF_TEGRA is not set ++# CONFIG_BCMDHD_QMONITOR is not set ++# CONFIG_BCMDHD_CUSTOM_NET_BW_EST_TEGRA is not set ++# CONFIG_BCMDHD_CUSTOM_NET_DIAG_TEGRA is not set ++CONFIG_BCM4359=y ++CONFIG_BCMDHD_PCIE_FW_PATH="/system/vendor/firmware/fw_bcmdhd.bin" ++CONFIG_BCMDHD_PCIE_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" ++CONFIG_BCMDHD_CLM_PATH="/lib/firmware/brcm/bcmdhd.clm_blob" ++CONFIG_BCMDHD_PCIE_ES4_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" ++CONFIG_DHD_SET_RANDOM_MAC_VAL=0x001A11 ++# CONFIG_RTL8812AU is not set ++# CONFIG_RTL8814AU is not set ++# CONFIG_RTL8821AU is not set ++# CONFIG_RTL8821CU is not set ++# CONFIG_RTL8822BU is not set + CONFIG_RTL8822CE=m ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_VMXNET3 is not set ++# CONFIG_FUJITSU_ES is not set ++# CONFIG_NETDEVSIM is not set ++CONFIG_NET_FAILOVER=y + CONFIG_TEGRA_HV_NET=y ++# CONFIG_ISDN is not set ++# CONFIG_NVM is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y + CONFIG_INPUT_LEDS=m ++CONFIG_INPUT_FF_MEMLESS=y + CONFIG_INPUT_POLLDEV=m ++# CONFIG_INPUT_SPARSEKMAP is not set ++CONFIG_INPUT_MATRIXKMAP=m ++ ++# ++# Userland interfaces ++# + CONFIG_INPUT_MOUSEDEV=y ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 + CONFIG_INPUT_JOYDEV=y + CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADC is not set + CONFIG_KEYBOARD_ADP5588=m + CONFIG_KEYBOARD_ADP5589=m + CONFIG_KEYBOARD_ATKBD=m ++# CONFIG_KEYBOARD_QT1050 is not set + CONFIG_KEYBOARD_QT1070=m + CONFIG_KEYBOARD_QT2160=m ++# CONFIG_KEYBOARD_DLINK_DIR685 is not set + CONFIG_KEYBOARD_LKKBD=m + CONFIG_KEYBOARD_GPIO=y + CONFIG_KEYBOARD_GPIO_POLLED=m +@@ -663,165 +3236,1225 @@ CONFIG_KEYBOARD_TEGRA=m + CONFIG_KEYBOARD_OPENCORES=m + CONFIG_KEYBOARD_SAMSUNG=m + CONFIG_KEYBOARD_STOWAWAY=m ++# CONFIG_KEYBOARD_SUNKBD is not set + CONFIG_KEYBOARD_OMAP4=m ++# CONFIG_KEYBOARD_TM2_TOUCHKEY is not set + CONFIG_KEYBOARD_XTKBD=m + CONFIG_KEYBOARD_CAP11XX=m + CONFIG_KEYBOARD_BCM=m ++# CONFIG_KEYBOARD_TIMED_GPIO is not set ++CONFIG_INPUT_MOUSE=y + CONFIG_MOUSE_PS2=m ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_BYD=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++CONFIG_MOUSE_PS2_FOCALTECH=y ++CONFIG_MOUSE_PS2_SMBUS=y + CONFIG_MOUSE_SERIAL=m + CONFIG_MOUSE_APPLETOUCH=m + CONFIG_MOUSE_BCM5974=m + CONFIG_MOUSE_CYAPA=m ++# CONFIG_MOUSE_ELAN_I2C is not set + CONFIG_MOUSE_VSXXXAA=m + CONFIG_MOUSE_GPIO=m + CONFIG_MOUSE_SYNAPTICS_I2C=m + CONFIG_MOUSE_SYNAPTICS_USB=m + CONFIG_INPUT_JOYSTICK=y ++# CONFIG_JOYSTICK_ANALOG is not set ++# CONFIG_JOYSTICK_A3D is not set ++# CONFIG_JOYSTICK_ADC is not set ++# CONFIG_JOYSTICK_ADI is not set ++# CONFIG_JOYSTICK_COBRA is not set ++# CONFIG_JOYSTICK_GF2K is not set ++# CONFIG_JOYSTICK_GRIP is not set ++# CONFIG_JOYSTICK_GRIP_MP is not set ++# CONFIG_JOYSTICK_GUILLEMOT is not set ++# CONFIG_JOYSTICK_INTERACT is not set ++# CONFIG_JOYSTICK_SIDEWINDER is not set ++# CONFIG_JOYSTICK_TMDC is not set ++# CONFIG_JOYSTICK_IFORCE is not set ++# CONFIG_JOYSTICK_WARRIOR is not set ++# CONFIG_JOYSTICK_MAGELLAN is not set ++# CONFIG_JOYSTICK_SPACEORB is not set ++# CONFIG_JOYSTICK_SPACEBALL is not set ++# CONFIG_JOYSTICK_STINGER is not set ++# CONFIG_JOYSTICK_TWIDJOY is not set ++# CONFIG_JOYSTICK_ZHENHUA is not set ++# CONFIG_JOYSTICK_AS5011 is not set ++# CONFIG_JOYSTICK_JOYDUMP is not set + CONFIG_JOYSTICK_XPAD=y ++# CONFIG_JOYSTICK_XPAD_FF is not set ++# CONFIG_JOYSTICK_XPAD_LEDS is not set ++# CONFIG_JOYSTICK_PSXPAD_SPI is not set ++# CONFIG_JOYSTICK_PXRC is not set ++# CONFIG_JOYSTICK_FSIA6B is not set + CONFIG_INPUT_TABLET=y ++# CONFIG_TABLET_USB_ACECAD is not set ++# CONFIG_TABLET_USB_AIPTEK is not set ++# CONFIG_TABLET_USB_GTCO is not set ++# CONFIG_TABLET_USB_HANWANG is not set ++# CONFIG_TABLET_USB_KBTAB is not set ++# CONFIG_TABLET_USB_PEGASUS is not set ++# CONFIG_TABLET_SERIAL_WACOM4 is not set + CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_PROPERTIES=y ++# CONFIG_TOUCHSCREEN_ADS7846 is not set ++# CONFIG_TOUCHSCREEN_AD7877 is not set ++# CONFIG_TOUCHSCREEN_AD7879 is not set ++# CONFIG_TOUCHSCREEN_ADC is not set ++# CONFIG_TOUCHSCREEN_AR1021_I2C is not set + CONFIG_TOUCHSCREEN_ATMEL_MXT=m ++# CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 is not set ++# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set ++# CONFIG_TOUCHSCREEN_BU21013 is not set ++# CONFIG_TOUCHSCREEN_BU21029 is not set ++# CONFIG_TOUCHSCREEN_CHIPONE_ICN8318 is not set ++# CONFIG_TOUCHSCREEN_CHIPONE_ICN8505 is not set ++# CONFIG_TOUCHSCREEN_CY8CTMA140 is not set ++# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set ++# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set ++# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set ++# CONFIG_TOUCHSCREEN_DYNAPRO is not set ++# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set ++# CONFIG_TOUCHSCREEN_EETI is not set ++# CONFIG_TOUCHSCREEN_EGALAX is not set ++# CONFIG_TOUCHSCREEN_EGALAX_SERIAL is not set ++# CONFIG_TOUCHSCREEN_EXC3000 is not set ++# CONFIG_TOUCHSCREEN_FUJITSU is not set ++# CONFIG_TOUCHSCREEN_GOODIX is not set ++# CONFIG_TOUCHSCREEN_HIDEEP is not set ++# CONFIG_TOUCHSCREEN_ILI210X is not set ++# CONFIG_TOUCHSCREEN_S6SY761 is not set ++# CONFIG_TOUCHSCREEN_GUNZE is not set ++# CONFIG_TOUCHSCREEN_EKTF2127 is not set ++# CONFIG_TOUCHSCREEN_ELAN is not set ++# CONFIG_TOUCHSCREEN_ELO is not set ++# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set ++# CONFIG_TOUCHSCREEN_WACOM_I2C is not set ++# CONFIG_TOUCHSCREEN_MAX11801 is not set ++# CONFIG_TOUCHSCREEN_MCS5000 is not set ++# CONFIG_TOUCHSCREEN_MMS114 is not set ++# CONFIG_TOUCHSCREEN_MELFAS_MIP4 is not set ++# CONFIG_TOUCHSCREEN_MTOUCH is not set ++# CONFIG_TOUCHSCREEN_IMX6UL_TSC is not set ++# CONFIG_TOUCHSCREEN_INEXIO is not set ++# CONFIG_TOUCHSCREEN_MK712 is not set ++# CONFIG_TOUCHSCREEN_PENMOUNT is not set ++# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set ++# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set ++# CONFIG_TOUCHSCREEN_TOUCHWIN is not set ++# CONFIG_TOUCHSCREEN_PIXCIR is not set ++# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set ++# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set ++# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set ++# CONFIG_TOUCHSCREEN_TSC_SERIO is not set ++# CONFIG_TOUCHSCREEN_TSC2004 is not set ++# CONFIG_TOUCHSCREEN_TSC2005 is not set ++# CONFIG_TOUCHSCREEN_TSC2007 is not set ++# CONFIG_TOUCHSCREEN_RM_TS is not set ++# CONFIG_TOUCHSCREEN_SILEAD is not set ++# CONFIG_TOUCHSCREEN_SIS_I2C is not set ++# CONFIG_TOUCHSCREEN_ST1232 is not set ++# CONFIG_TOUCHSCREEN_STMFTS is not set ++# CONFIG_TOUCHSCREEN_SUR40 is not set ++# CONFIG_TOUCHSCREEN_SURFACE3_SPI is not set ++# CONFIG_TOUCHSCREEN_SX8654 is not set ++# CONFIG_TOUCHSCREEN_TPS6507X is not set ++# CONFIG_TOUCHSCREEN_ZET6223 is not set ++# CONFIG_TOUCHSCREEN_ZFORCE is not set ++# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set ++# CONFIG_TOUCHSCREEN_IQS5XX is not set ++# CONFIG_TOUCHSCREEN_ZINITIX is not set ++# CONFIG_TOUCHSCREEN_NVIDIA_ATMEL_MXT is not set ++# CONFIG_TOUCHSCREEN_LR388K7 is not set ++# CONFIG_TOUCHSCREEN_RM31080A is not set ++# CONFIG_TOUCHSCREEN_EXC80 is not set ++# CONFIG_TOUCHSCREEN_EXC80_USB is not set + CONFIG_INPUT_MISC=y ++# CONFIG_INPUT_AD714X is not set ++# CONFIG_INPUT_ATMEL_CAPTOUCH is not set ++# CONFIG_INPUT_BMA150 is not set ++# CONFIG_INPUT_E3X0_BUTTON is not set ++# CONFIG_INPUT_MMA8450 is not set ++# CONFIG_INPUT_GPIO_BEEPER is not set ++# CONFIG_INPUT_GPIO_DECODER is not set ++# CONFIG_INPUT_GPIO_VIBRA is not set ++# CONFIG_INPUT_ATI_REMOTE2 is not set ++# CONFIG_INPUT_KEYSPAN_REMOTE is not set ++# CONFIG_INPUT_KXTJ9 is not set ++# CONFIG_INPUT_POWERMATE is not set ++# CONFIG_INPUT_YEALINK is not set ++# CONFIG_INPUT_CM109 is not set ++# CONFIG_INPUT_REGULATOR_HAPTIC is not set + CONFIG_INPUT_UINPUT=y ++# CONFIG_INPUT_PCF8574 is not set ++# CONFIG_INPUT_PWM_BEEPER is not set ++# CONFIG_INPUT_PWM_VIBRA is not set ++# CONFIG_INPUT_RK805_PWRKEY is not set ++# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set ++# CONFIG_INPUT_ADXL34X is not set ++# CONFIG_INPUT_IMS_PCU is not set ++# CONFIG_INPUT_IQS269A is not set ++# CONFIG_INPUT_CMA3000 is not set ++# CONFIG_INPUT_SOC_BUTTON_ARRAY is not set ++# CONFIG_INPUT_DRV260X_HAPTICS is not set ++# CONFIG_INPUT_DRV2665_HAPTICS is not set ++# CONFIG_INPUT_DRV2667_HAPTICS is not set ++CONFIG_RMI4_CORE=m ++# CONFIG_RMI4_I2C is not set ++# CONFIG_RMI4_SPI is not set ++# CONFIG_RMI4_SMB is not set ++CONFIG_RMI4_F03=y ++CONFIG_RMI4_F03_SERIO=m ++CONFIG_RMI4_2D_SENSOR=y ++CONFIG_RMI4_F11=y ++CONFIG_RMI4_F12=y ++CONFIG_RMI4_F30=y ++# CONFIG_RMI4_F34 is not set ++# CONFIG_RMI4_F3A is not set ++# CONFIG_RMI4_F54 is not set ++# CONFIG_RMI4_F55 is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y + CONFIG_SERIO_SERPORT=m + CONFIG_SERIO_AMBAKMI=m ++# CONFIG_SERIO_PCIPS2 is not set ++CONFIG_SERIO_LIBPS2=m ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_SERIO_GPIO_PS2 is not set ++# CONFIG_USERIO is not set ++# CONFIG_GAMEPORT is not set ++# end of Hardware I/O ports ++# end of Input device support ++ ++# CONFIG_INPUT_CFBOOST is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++CONFIG_UNIX98_PTYS=y ++CONFIG_LEGACY_PTYS=y + CONFIG_LEGACY_PTY_COUNT=16 ++CONFIG_LDISC_AUTOLOAD=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y + CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y ++CONFIG_SERIAL_8250_PNP=y ++CONFIG_SERIAL_8250_16550A_VARIANTS=y ++# CONFIG_SERIAL_8250_FINTEK is not set + CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_DMA=y ++CONFIG_SERIAL_8250_PCI=y ++CONFIG_SERIAL_8250_EXAR=y ++CONFIG_SERIAL_8250_NR_UARTS=4 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=4 + CONFIG_SERIAL_8250_EXTENDED=y ++# CONFIG_SERIAL_8250_MANY_PORTS is not set ++# CONFIG_SERIAL_8250_ASPEED_VUART is not set + CONFIG_SERIAL_8250_SHARE_IRQ=y ++# CONFIG_SERIAL_8250_DETECT_IRQ is not set ++# CONFIG_SERIAL_8250_RSA is not set ++CONFIG_SERIAL_8250_DWLIB=y ++CONFIG_SERIAL_8250_FSL=y + CONFIG_SERIAL_8250_DW=m ++# CONFIG_SERIAL_8250_RT288X is not set ++CONFIG_SERIAL_8250_TEGRA=y + CONFIG_SERIAL_OF_PLATFORM=y ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set + CONFIG_SERIAL_AMBA_PL011=y + CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set + CONFIG_SERIAL_TEGRA=y + CONFIG_SERIAL_TEGRA_TCU=y ++CONFIG_SERIAL_TEGRA_TCU_CONSOLE=y ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++# CONFIG_SERIAL_UARTLITE is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_JSM is not set ++# CONFIG_SERIAL_SIFIVE is not set ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set + CONFIG_SERIAL_XILINX_PS_UART=y ++# CONFIG_SERIAL_XILINX_PS_UART_CONSOLE is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_RP2 is not set + CONFIG_SERIAL_FSL_LPUART=y ++# CONFIG_SERIAL_FSL_LPUART_CONSOLE is not set ++# CONFIG_SERIAL_FSL_LINFLEXUART is not set ++# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set ++# CONFIG_SERIAL_SPRD is not set ++# end of Serial drivers ++ ++CONFIG_SERIAL_MCTRL_GPIO=y + CONFIG_TEGRA_COMBINED_UART_EARLY=y ++# CONFIG_TEGRA_HV_COMM is not set ++# CONFIG_SERIAL_TEGRA_NOHW is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_NOZOMI is not set ++# CONFIG_NULL_TTY is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_HVC_DRIVER=y ++# CONFIG_HVC_DCC is not set + CONFIG_SERIAL_DEV_BUS=y ++CONFIG_SERIAL_DEV_CTRL_TTYPORT=y ++# CONFIG_TTY_PRINTK is not set + CONFIG_VIRTIO_CONSOLE=y ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_IPMB_DEVICE_INTERFACE is not set ++CONFIG_HW_RANDOM=m ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_HW_RANDOM_BA431 is not set ++# CONFIG_HW_RANDOM_VIRTIO is not set ++CONFIG_HW_RANDOM_HISI_V2=m ++CONFIG_HW_RANDOM_CAVIUM=m ++CONFIG_HW_RANDOM_OPTEE=m ++# CONFIG_HW_RANDOM_CCTRNG is not set ++# CONFIG_HW_RANDOM_XIPHERA is not set ++# CONFIG_APPLICOM is not set ++CONFIG_DEVMEM=y ++# CONFIG_RAW_DRIVER is not set ++CONFIG_DEVPORT=y ++# CONFIG_TCG_TPM is not set ++# CONFIG_XILLYBUS is not set ++# end of Character devices ++ ++# CONFIG_RANDOM_TRUST_CPU is not set ++# CONFIG_RANDOM_TRUST_BOOTLOADER is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_ACPI_I2C_OPREGION=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y + CONFIG_I2C_CHARDEV=y + CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set + CONFIG_I2C_MUX_GPIO=y ++# CONFIG_I2C_MUX_GPMUX is not set ++# CONFIG_I2C_MUX_LTC4306 is not set ++# CONFIG_I2C_MUX_PCA9541 is not set + CONFIG_I2C_MUX_PCA954x=y ++# CONFIG_I2C_MUX_PINCTRL is not set ++# CONFIG_I2C_MUX_REG is not set ++# CONFIG_I2C_DEMUX_PINCTRL is not set ++# CONFIG_I2C_MUX_MLXCPLD is not set ++# end of Multiplexer I2C Chip support ++ ++CONFIG_I2C_HELPER_AUTO=y ++CONFIG_I2C_SMBUS=m ++CONFIG_I2C_ALGOBIT=y ++CONFIG_I2C_ALGOPCA=m ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# PC SMBus host controller drivers ++# + CONFIG_I2C_ALI1535=m + CONFIG_I2C_ALI1563=m + CONFIG_I2C_ALI15X3=m + CONFIG_I2C_AMD756=m + CONFIG_I2C_AMD8111=m ++# CONFIG_I2C_AMD_MP2 is not set + CONFIG_I2C_I801=m + CONFIG_I2C_ISCH=m + CONFIG_I2C_PIIX4=m + CONFIG_I2C_NFORCE2=m ++# CONFIG_I2C_NVIDIA_GPU is not set ++# CONFIG_I2C_SIS5595 is not set + CONFIG_I2C_SIS630=m ++# CONFIG_I2C_SIS96X is not set ++# CONFIG_I2C_VIA is not set + CONFIG_I2C_VIAPRO=m ++ ++# ++# ACPI drivers ++# ++# CONFIG_I2C_SCMI is not set ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# + CONFIG_I2C_CADENCE=m + CONFIG_I2C_CBUS_GPIO=m ++CONFIG_I2C_DESIGNWARE_CORE=m ++# CONFIG_I2C_DESIGNWARE_SLAVE is not set + CONFIG_I2C_DESIGNWARE_PLATFORM=m + CONFIG_I2C_DESIGNWARE_PCI=m + CONFIG_I2C_EMEV2=m + CONFIG_I2C_GPIO=m ++# CONFIG_I2C_GPIO_FAULT_INJECTOR is not set + CONFIG_I2C_NOMADIK=m + CONFIG_I2C_OCORES=m + CONFIG_I2C_PCA_PLATFORM=m + CONFIG_I2C_RK3X=m + CONFIG_I2C_SIMTEC=m + CONFIG_I2C_TEGRA=y ++CONFIG_I2C_TEGRA_BPMP=y ++# CONFIG_I2C_THUNDERX is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# + CONFIG_I2C_DIOLAN_U2C=m ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_TEGRA_VI is not set + CONFIG_I2C_NVVRS11=m + CONFIG_I2C_TEGRA_CAMRTC=y ++CONFIG_I2C_TEGRA_HV=y + CONFIG_I2C_TEGRA_SLAVE=m + CONFIG_I2C_TEGRA194_SLAVE=m ++# end of I2C Hardware Bus support ++ + CONFIG_I2C_STUB=m ++CONFIG_I2C_SLAVE=y + CONFIG_I2C_SLAVE_EEPROM=m ++# CONFIG_I2C_SLAVE_TESTUNIT is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++# end of I2C support ++ ++# CONFIG_I3C is not set + CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++CONFIG_SPI_MEM=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_AXI_SPI_ENGINE is not set ++# CONFIG_SPI_BITBANG is not set + CONFIG_SPI_CADENCE=m + CONFIG_SPI_CADENCE_QUADSPI=y ++# CONFIG_SPI_DESIGNWARE is not set ++# CONFIG_SPI_HISI_SFC_V3XX is not set + CONFIG_SPI_NXP_FLEXSPI=y ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++# CONFIG_SPI_PL022 is not set + CONFIG_SPI_PXA2XX=m ++CONFIG_SPI_PXA2XX_PCI=m ++# CONFIG_SPI_ROCKCHIP is not set + CONFIG_SPI_SC18IS602=m ++# CONFIG_SPI_SIFIVE is not set ++# CONFIG_SPI_MXIC is not set + CONFIG_SPI_TEGRA114=m + CONFIG_SPI_TEGRA124_SLAVE=y + CONFIG_SPI_TEGRA194_SLAVE=y ++# CONFIG_SPI_TEGRA20_SFLASH is not set ++# CONFIG_SPI_TEGRA20_SLINK is not set + CONFIG_QSPI_TEGRA23x=y + CONFIG_QSPI_TEGRA210=m ++CONFIG_QSPI_TEGRA=y ++# CONFIG_SPI_THUNDERX is not set + CONFIG_SPI_XCOMM=m ++# CONFIG_SPI_XILINX is not set + CONFIG_SPI_ZYNQMP_GQSPI=m ++# CONFIG_SPI_AMD is not set ++ ++# ++# SPI Multiplexer support ++# ++# CONFIG_SPI_MUX is not set ++ ++# ++# SPI Protocol Masters ++# + CONFIG_SPI_SPIDEV=m ++# CONFIG_SPI_LOOPBACK_TEST is not set + CONFIG_SPI_TLE62X0=m ++# CONFIG_SPI_SLAVE is not set ++CONFIG_SPI_DYNAMIC=y ++# CONFIG_SPI_AURIX_TEGRA is not set + CONFIG_SPMI=m ++# CONFIG_HSI is not set ++CONFIG_PPS=y + CONFIG_PPS_DEBUG=y ++ ++# ++# PPS clients support ++# ++# CONFIG_PPS_CLIENT_KTIMER is not set ++# CONFIG_PPS_CLIENT_LDISC is not set + CONFIG_PPS_CLIENT_GPIO=y ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++CONFIG_PTP_1588_CLOCK=y ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++# CONFIG_PTP_1588_CLOCK_IDT82P33 is not set ++# CONFIG_PTP_1588_CLOCK_IDTCM is not set ++# end of PTP clock support ++ ++CONFIG_PINCTRL=y ++CONFIG_PINCTRL_TEGRA186=y + CONFIG_PINCTRL_TEGRA186_DPAUX=y ++CONFIG_PINCTRL_TEGRA194=y + CONFIG_PINCTRL_TEGRA234_DPAUX=y ++CONFIG_PINCTRL_TEGRA234=y ++CONFIG_GENERIC_PINCTRL_GROUPS=y ++CONFIG_PINMUX=y ++CONFIG_GENERIC_PINMUX_FUNCTIONS=y ++CONFIG_PINCONF=y ++CONFIG_GENERIC_PINCONF=y ++# CONFIG_DEBUG_PINCTRL is not set ++# CONFIG_PINCTRL_AMD is not set ++# CONFIG_PINCTRL_MCP23S08 is not set + CONFIG_PINCTRL_SINGLE=y ++# CONFIG_PINCTRL_SX150X is not set ++# CONFIG_PINCTRL_STMFX is not set + CONFIG_PINCTRL_MAX77620=y ++# CONFIG_PINCTRL_RK805 is not set ++# CONFIG_PINCTRL_OCELOT is not set ++ ++# ++# Renesas pinctrl drivers ++# ++# end of Renesas pinctrl drivers ++ ++CONFIG_PINCTRL_TEGRA=y ++CONFIG_PINCTRL_TEGRA210=y ++CONFIG_PINCTRL_TEGRA_XUSB=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIOLIB_FASTPATH_LIMIT=512 ++CONFIG_OF_GPIO=y ++CONFIG_GPIO_ACPI=y ++CONFIG_GPIOLIB_IRQCHIP=y ++CONFIG_DEBUG_GPIO=y + CONFIG_GPIO_SYSFS=y ++CONFIG_GPIO_CDEV=y ++CONFIG_GPIO_CDEV_V1=y ++CONFIG_GPIO_GENERIC=y ++ ++# ++# Memory mapped GPIO drivers ++# ++# CONFIG_GPIO_74XX_MMIO is not set ++# CONFIG_GPIO_ALTERA is not set ++# CONFIG_GPIO_AMDPT is not set ++# CONFIG_GPIO_CADENCE is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_EXAR is not set ++# CONFIG_GPIO_FTGPIO010 is not set ++CONFIG_GPIO_GENERIC_PLATFORM=y ++# CONFIG_GPIO_GRGPIO is not set ++# CONFIG_GPIO_HLWD is not set ++# CONFIG_GPIO_LOGICVC is not set + CONFIG_GPIO_MB86S7X=y ++# CONFIG_GPIO_PL061 is not set ++# CONFIG_GPIO_SAMA5D2_PIOBU is not set ++# CONFIG_GPIO_SIFIVE is not set ++# CONFIG_GPIO_SYSCON is not set ++CONFIG_GPIO_TEGRA=y ++CONFIG_GPIO_TEGRA186=y ++# CONFIG_GPIO_XGENE is not set ++# CONFIG_GPIO_XILINX is not set ++# CONFIG_GPIO_AMD_FCH is not set ++# end of Memory mapped GPIO drivers ++ ++# ++# I2C GPIO expanders ++# ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++# CONFIG_GPIO_GW_PLD is not set ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set + CONFIG_GPIO_PCA953X=y + CONFIG_GPIO_PCA953X_IRQ=y ++# CONFIG_GPIO_PCA9570 is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_TPIC2810 is not set ++# end of I2C GPIO expanders ++ ++# ++# MFD GPIO expanders ++# + CONFIG_GPIO_BD9571MWV=m + CONFIG_GPIO_MAX77620=y ++# end of MFD GPIO expanders ++ ++# ++# PCI GPIO expanders ++# ++# CONFIG_GPIO_BT8XX is not set ++# CONFIG_GPIO_PCI_IDIO_16 is not set ++# CONFIG_GPIO_PCIE_IDIO_24 is not set ++# CONFIG_GPIO_RDC321X is not set ++# end of PCI GPIO expanders ++ ++# ++# SPI GPIO expanders ++# ++# CONFIG_GPIO_74X164 is not set ++# CONFIG_GPIO_MAX3191X is not set ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_PISOSR is not set ++# CONFIG_GPIO_XRA1403 is not set ++# end of SPI GPIO expanders ++ ++# ++# USB GPIO expanders ++# ++# end of USB GPIO expanders ++ ++# CONFIG_GPIO_AGGREGATOR is not set ++CONFIG_GPIO_MOCKUP=y + CONFIG_GPIO_TMPM32X_I2C=y ++# CONFIG_W1 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set + CONFIG_POWER_RESET_MAX77620=y ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VEXPRESS is not set ++# CONFIG_POWER_RESET_XGENE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_RESET_SYSCON_POWEROFF is not set ++# CONFIG_SYSCON_REBOOT_MODE is not set ++# CONFIG_NVMEM_REBOOT_MODE is not set ++CONFIG_SYSTEM_PMIC=y ++# CONFIG_POWER_OFF_TMPM32X_I2C is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++CONFIG_POWER_SUPPLY_HWMON=y ++# CONFIG_PDA_POWER is not set ++# CONFIG_GENERIC_ADC_BATTERY is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_CHARGER_ADP5061 is not set ++# CONFIG_BATTERY_CW2015 is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set + CONFIG_BATTERY_SBS=m ++# CONFIG_CHARGER_SBS is not set ++# CONFIG_MANAGER_SBS is not set + CONFIG_BATTERY_BQ27XXX=y ++CONFIG_BATTERY_BQ27XXX_I2C=y ++# CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_ISP1704 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_MANAGER is not set ++# CONFIG_CHARGER_LT3651 is not set ++# CONFIG_CHARGER_DETECTOR_MAX14656 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24257 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_BQ2515X is not set ++# CONFIG_CHARGER_BQ25890 is not set ++# CONFIG_CHARGER_BQ25980 is not set ++# CONFIG_CHARGER_SMB347 is not set ++# CONFIG_BATTERY_GAUGE_LTC2941 is not set ++# CONFIG_BATTERY_RT5033 is not set ++# CONFIG_CHARGER_RT9455 is not set ++# CONFIG_CHARGER_UCS1002 is not set ++# CONFIG_CHARGER_BD99954 is not set ++CONFIG_HWMON=y ++# CONFIG_HWMON_DEBUG_CHIP is not set ++ ++# ++# Native drivers ++# ++# CONFIG_SENSORS_AD7314 is not set ++# CONFIG_SENSORS_AD7414 is not set ++# CONFIG_SENSORS_AD7418 is not set ++# CONFIG_SENSORS_ADM1021 is not set ++# CONFIG_SENSORS_ADM1025 is not set ++# CONFIG_SENSORS_ADM1026 is not set ++# CONFIG_SENSORS_ADM1029 is not set ++# CONFIG_SENSORS_ADM1031 is not set ++# CONFIG_SENSORS_ADM1177 is not set ++# CONFIG_SENSORS_ADM9240 is not set ++# CONFIG_SENSORS_ADT7310 is not set ++# CONFIG_SENSORS_ADT7410 is not set ++# CONFIG_SENSORS_ADT7411 is not set ++# CONFIG_SENSORS_ADT7462 is not set ++# CONFIG_SENSORS_ADT7470 is not set ++# CONFIG_SENSORS_ADT7475 is not set ++# CONFIG_SENSORS_AS370 is not set ++# CONFIG_SENSORS_ASC7621 is not set ++# CONFIG_SENSORS_AXI_FAN_CONTROL is not set ++# CONFIG_SENSORS_ARM_SCPI is not set ++# CONFIG_SENSORS_ASPEED is not set ++# CONFIG_SENSORS_ATXP1 is not set ++# CONFIG_SENSORS_CORSAIR_CPRO is not set ++# CONFIG_SENSORS_DRIVETEMP is not set ++# CONFIG_SENSORS_DS620 is not set ++# CONFIG_SENSORS_DS1621 is not set ++# CONFIG_SENSORS_I5K_AMB is not set ++# CONFIG_SENSORS_F71805F is not set ++# CONFIG_SENSORS_F71882FG is not set ++# CONFIG_SENSORS_F75375S is not set ++# CONFIG_SENSORS_FTSTEUTATES is not set ++# CONFIG_SENSORS_GL518SM is not set ++# CONFIG_SENSORS_GL520SM is not set ++# CONFIG_SENSORS_G760A is not set ++# CONFIG_SENSORS_G762 is not set ++# CONFIG_SENSORS_GPIO_FAN is not set ++# CONFIG_SENSORS_HIH6130 is not set ++# CONFIG_SENSORS_IIO_HWMON is not set ++# CONFIG_SENSORS_IT87 is not set ++# CONFIG_SENSORS_JC42 is not set ++# CONFIG_SENSORS_POWR1220 is not set ++# CONFIG_SENSORS_LINEAGE is not set ++# CONFIG_SENSORS_LTC2945 is not set ++# CONFIG_SENSORS_LTC2947_I2C is not set ++# CONFIG_SENSORS_LTC2947_SPI is not set ++# CONFIG_SENSORS_LTC2990 is not set ++# CONFIG_SENSORS_LTC4151 is not set ++# CONFIG_SENSORS_LTC4215 is not set ++# CONFIG_SENSORS_LTC4222 is not set ++# CONFIG_SENSORS_LTC4245 is not set ++# CONFIG_SENSORS_LTC4260 is not set ++# CONFIG_SENSORS_LTC4261 is not set ++# CONFIG_SENSORS_MAX1111 is not set ++# CONFIG_SENSORS_MAX16065 is not set ++# CONFIG_SENSORS_MAX1619 is not set ++# CONFIG_SENSORS_MAX1668 is not set ++# CONFIG_SENSORS_MAX197 is not set ++# CONFIG_SENSORS_MAX31722 is not set ++# CONFIG_SENSORS_MAX31730 is not set ++# CONFIG_SENSORS_MAX6621 is not set ++# CONFIG_SENSORS_MAX6639 is not set ++# CONFIG_SENSORS_MAX6642 is not set ++# CONFIG_SENSORS_MAX6650 is not set ++# CONFIG_SENSORS_MAX6697 is not set ++# CONFIG_SENSORS_MAX31790 is not set ++# CONFIG_SENSORS_MCP3021 is not set ++# CONFIG_SENSORS_TC654 is not set ++# CONFIG_SENSORS_MR75203 is not set ++# CONFIG_SENSORS_ADCXX is not set ++# CONFIG_SENSORS_LM63 is not set ++# CONFIG_SENSORS_LM70 is not set ++# CONFIG_SENSORS_LM73 is not set ++# CONFIG_SENSORS_LM75 is not set ++# CONFIG_SENSORS_LM77 is not set ++# CONFIG_SENSORS_LM78 is not set ++# CONFIG_SENSORS_LM80 is not set ++# CONFIG_SENSORS_LM83 is not set ++# CONFIG_SENSORS_LM85 is not set ++# CONFIG_SENSORS_LM87 is not set ++# CONFIG_SENSORS_LM90 is not set ++# CONFIG_SENSORS_LM92 is not set ++# CONFIG_SENSORS_LM93 is not set ++# CONFIG_SENSORS_LM95234 is not set ++# CONFIG_SENSORS_LM95241 is not set ++# CONFIG_SENSORS_LM95245 is not set ++# CONFIG_SENSORS_PC87360 is not set ++# CONFIG_SENSORS_PC87427 is not set ++# CONFIG_SENSORS_NTC_THERMISTOR is not set ++# CONFIG_SENSORS_NCT6683 is not set ++# CONFIG_SENSORS_NCT6775 is not set ++# CONFIG_SENSORS_NCT7802 is not set ++# CONFIG_SENSORS_NCT7904 is not set ++# CONFIG_SENSORS_NPCM7XX is not set ++# CONFIG_SENSORS_OCC_P8_I2C is not set ++# CONFIG_SENSORS_PCF8591 is not set ++# CONFIG_PMBUS is not set + CONFIG_SENSORS_PWM_FAN=m ++# CONFIG_SENSORS_SHT15 is not set ++# CONFIG_SENSORS_SHT21 is not set ++# CONFIG_SENSORS_SHT3x is not set ++# CONFIG_SENSORS_SHTC1 is not set ++# CONFIG_SENSORS_SIS5595 is not set ++# CONFIG_SENSORS_DME1737 is not set ++# CONFIG_SENSORS_EMC1403 is not set ++# CONFIG_SENSORS_EMC2103 is not set ++# CONFIG_SENSORS_EMC6W201 is not set ++# CONFIG_SENSORS_SMSC47M1 is not set ++# CONFIG_SENSORS_SMSC47M192 is not set ++# CONFIG_SENSORS_SMSC47B397 is not set ++# CONFIG_SENSORS_SCH5627 is not set ++# CONFIG_SENSORS_SCH5636 is not set ++# CONFIG_SENSORS_STTS751 is not set ++# CONFIG_SENSORS_SMM665 is not set ++# CONFIG_SENSORS_ADC128D818 is not set ++# CONFIG_SENSORS_ADS7828 is not set ++# CONFIG_SENSORS_ADS7871 is not set ++# CONFIG_SENSORS_AMC6821 is not set ++# CONFIG_SENSORS_INA209 is not set + CONFIG_SENSORS_INA2XX=m + CONFIG_SENSORS_INA3221=m ++# CONFIG_SENSORS_TC74 is not set ++# CONFIG_SENSORS_THMC50 is not set ++# CONFIG_SENSORS_TMP102 is not set ++# CONFIG_SENSORS_TMP103 is not set ++# CONFIG_SENSORS_TMP108 is not set ++# CONFIG_SENSORS_TMP401 is not set ++# CONFIG_SENSORS_TMP421 is not set ++# CONFIG_SENSORS_TMP513 is not set ++# CONFIG_SENSORS_VEXPRESS is not set ++# CONFIG_SENSORS_VIA686A is not set ++# CONFIG_SENSORS_VT1211 is not set ++# CONFIG_SENSORS_VT8231 is not set ++# CONFIG_SENSORS_W83773G is not set ++# CONFIG_SENSORS_W83781D is not set ++# CONFIG_SENSORS_W83791D is not set ++# CONFIG_SENSORS_W83792D is not set ++# CONFIG_SENSORS_W83793 is not set ++# CONFIG_SENSORS_W83795 is not set ++# CONFIG_SENSORS_W83L785TS is not set ++# CONFIG_SENSORS_W83L786NG is not set ++# CONFIG_SENSORS_W83627HF is not set ++# CONFIG_SENSORS_W83627EHF is not set ++# CONFIG_SENSORS_XGENE is not set + CONFIG_GPIO_TACHOMETER=y ++ ++# ++# ACPI drivers ++# ++# CONFIG_SENSORS_ACPI_POWER is not set ++ ++# ++# HWMON devices ++# ++CONFIG_SENSORS_F75308=m ++# end of HWMON devices ++ ++CONFIG_THERMAL=y ++# CONFIG_THERMAL_NETLINK is not set ++# CONFIG_THERMAL_STATISTICS is not set ++CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 ++CONFIG_THERMAL_HWMON=y ++CONFIG_THERMAL_OF=y + CONFIG_THERMAL_WRITABLE_TRIPS=y ++CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y ++# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set ++# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set ++# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set ++# CONFIG_THERMAL_GOV_FAIR_SHARE is not set ++CONFIG_THERMAL_GOV_STEP_WISE=y ++# CONFIG_THERMAL_GOV_BANG_BANG is not set ++# CONFIG_THERMAL_GOV_USER_SPACE is not set + CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y + CONFIG_CPU_THERMAL=y ++CONFIG_CPU_FREQ_THERMAL=y + CONFIG_DEVFREQ_THERMAL=y + CONFIG_THERMAL_EMULATION=y ++# CONFIG_THERMAL_MMIO is not set + CONFIG_MAX77620_THERMAL=m ++ ++# ++# NVIDIA Tegra thermal drivers ++# + CONFIG_TEGRA_SOCTHERM=y + CONFIG_TEGRA_BPMP_THERMAL=m + CONFIG_TEGRA_AOTAG=y + CONFIG_TEGRA_TJ_THERMAL=y + CONFIG_TEGRA_CORE_CAPS=y + CONFIG_TEGRA_DFLL_CAPS=y ++# end of NVIDIA Tegra thermal drivers ++ ++# CONFIG_GENERIC_ADC_THERMAL is not set ++ ++# ++# Nvidia Thermal Drivers ++# ++# CONFIG_PWM_FAN is not set + CONFIG_THERMAL_GOV_PID=y +-CONFIG_USERSPACE_THERM_ALERT=m ++# CONFIG_THERMAL_GOV_CONTINUOUS is not set ++# CONFIG_TEGRA_THERMAL_THROTTLE is not set + CONFIG_TEGRA23X_OC_EVENT=y + CONFIG_TEGRA19X_OC_EVENT=y ++CONFIG_USERSPACE_THERM_ALERT=m ++# end of Nvidia Thermal Drivers ++ + CONFIG_WATCHDOG=y ++CONFIG_WATCHDOG_CORE=y + CONFIG_WATCHDOG_NOWAYOUT=y ++CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y ++CONFIG_WATCHDOG_OPEN_TIMEOUT=0 ++# CONFIG_WATCHDOG_SYSFS is not set ++ ++# ++# Watchdog Pretimeout Governors ++# ++# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set ++ ++# ++# Watchdog Device Drivers ++# ++# CONFIG_SOFT_WATCHDOG is not set ++# CONFIG_SOFT_PLATFORM_WATCHDOG is not set ++# CONFIG_GPIO_WATCHDOG is not set ++# CONFIG_WDAT_WDT is not set ++# CONFIG_XILINX_WATCHDOG is not set ++# CONFIG_ZIIRAVE_WATCHDOG is not set ++# CONFIG_ARM_SP805_WATCHDOG is not set ++# CONFIG_ARM_SBSA_WATCHDOG is not set ++# CONFIG_CADENCE_WATCHDOG is not set ++# CONFIG_DW_WATCHDOG is not set ++# CONFIG_MAX63XX_WATCHDOG is not set + CONFIG_MAX77620_WATCHDOG=y ++# CONFIG_TEGRA_WATCHDOG_LEGACY is not set ++# CONFIG_TEGRA_WATCHDOG is not set + CONFIG_ARM_SMC_WATCHDOG=y ++# CONFIG_ALIM7101_WDT is not set ++# CONFIG_I6300ESB_WDT is not set ++# CONFIG_MEN_A21_WDT is not set ++ ++# ++# PCI-based Watchdog Cards ++# ++# CONFIG_PCIPCWATCHDOG is not set ++# CONFIG_WDTPCI is not set ++ ++# ++# USB-based Watchdog Cards ++# ++# CONFIG_USBPCWATCHDOG is not set + CONFIG_TEGRA21X_WATCHDOG=y + CONFIG_TEGRA18X_WATCHDOG=y + CONFIG_TEGRA_HV_WATCHDOG=y ++CONFIG_SSB_POSSIBLE=y ++CONFIG_SSB=m ++CONFIG_SSB_SPROM=y ++CONFIG_SSB_BLOCKIO=y ++CONFIG_SSB_PCIHOST_POSSIBLE=y ++CONFIG_SSB_PCIHOST=y ++CONFIG_SSB_B43_PCI_BRIDGE=y ++CONFIG_SSB_SDIOHOST_POSSIBLE=y ++CONFIG_SSB_SDIOHOST=y ++CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y ++CONFIG_SSB_DRIVER_PCICORE=y ++# CONFIG_SSB_DRIVER_GPIO is not set ++CONFIG_BCMA_POSSIBLE=y ++CONFIG_BCMA=m ++CONFIG_BCMA_BLOCKIO=y ++CONFIG_BCMA_HOST_PCI_POSSIBLE=y ++CONFIG_BCMA_HOST_PCI=y ++# CONFIG_BCMA_HOST_SOC is not set ++CONFIG_BCMA_DRIVER_PCI=y ++# CONFIG_BCMA_DRIVER_GMAC_CMN is not set ++# CONFIG_BCMA_DRIVER_GPIO is not set ++# CONFIG_BCMA_DEBUG is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_ACT8945A is not set ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_ATMEL_FLEXCOM is not set ++# CONFIG_MFD_ATMEL_HLCDC is not set ++# CONFIG_MFD_BCM590XX is not set + CONFIG_MFD_BD9571MWV=y ++# CONFIG_MFD_AXP20X_I2C is not set ++# CONFIG_MFD_MADERA is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9062 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_DA9150 is not set ++# CONFIG_MFD_DLN2 is not set ++# CONFIG_MFD_GATEWORKS_GSC is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_MP2629 is not set + CONFIG_MFD_HI6421_PMIC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_LPC_ICH is not set ++CONFIG_LPC_SCH=m ++# CONFIG_MFD_IQS62X is not set ++# CONFIG_MFD_JANZ_CMODIO is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set + CONFIG_MFD_MAX77620=y ++# CONFIG_MFD_MAX77650 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX77843 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MT6360 is not set ++# CONFIG_MFD_MT6397 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_CPCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_RDC321X is not set ++# CONFIG_MFD_RT5033 is not set ++# CONFIG_MFD_RC5T583 is not set + CONFIG_MFD_RK808=y ++# CONFIG_MFD_RN5T618 is not set + CONFIG_MFD_SEC_CORE=y ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SKY81452 is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_TI_LMU is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65086 is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TI_LP873X is not set ++# CONFIG_MFD_TI_LP87565 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TQMX86 is not set ++# CONFIG_MFD_VX855 is not set ++# CONFIG_MFD_LOCHNAGAR is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set + CONFIG_MFD_ROHM_BD718XX=y ++# CONFIG_MFD_ROHM_BD70528 is not set ++# CONFIG_MFD_ROHM_BD71828 is not set ++# CONFIG_MFD_STPMIC1 is not set ++# CONFIG_MFD_STMFX is not set ++CONFIG_MFD_VEXPRESS_SYSREG=y ++# CONFIG_RAVE_SP_CORE is not set ++# CONFIG_MFD_INTEL_M10_BMC is not set ++# end of Multifunction device drivers ++ ++CONFIG_MFD_TMPM32X_I2C=y + CONFIG_MFD_NVVRS_PSEQ=y +-CONFIG_NVVRS_PSEQ_RTC=y + CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set + CONFIG_REGULATOR_FIXED_VOLTAGE=y ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_88PG86X is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_BD718XX is not set ++# CONFIG_REGULATOR_BD9571MWV is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_FAN53880 is not set + CONFIG_REGULATOR_GPIO=y ++# CONFIG_REGULATOR_HI6421 is not set ++# CONFIG_REGULATOR_HI6421V530 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_LTC3676 is not set ++# CONFIG_REGULATOR_MAX1586 is not set + CONFIG_REGULATOR_MAX77620=y ++# CONFIG_REGULATOR_MAX16989 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_MAX77812 is not set ++# CONFIG_REGULATOR_MAX77826 is not set ++# CONFIG_REGULATOR_MCP16502 is not set ++# CONFIG_REGULATOR_MP5416 is not set ++# CONFIG_REGULATOR_MP8859 is not set ++# CONFIG_REGULATOR_MP886X is not set ++# CONFIG_REGULATOR_MPQ7920 is not set ++# CONFIG_REGULATOR_MT6311 is not set ++# CONFIG_REGULATOR_PCA9450 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_PV88060 is not set ++# CONFIG_REGULATOR_PV88080 is not set ++# CONFIG_REGULATOR_PV88090 is not set + CONFIG_REGULATOR_PWM=y ++# CONFIG_REGULATOR_QCOM_SPMI is not set ++# CONFIG_REGULATOR_QCOM_USB_VBUS is not set ++# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set ++# CONFIG_REGULATOR_RK808 is not set ++# CONFIG_REGULATOR_RT4801 is not set ++# CONFIG_REGULATOR_RTMV20 is not set ++# CONFIG_REGULATOR_S2MPA01 is not set ++# CONFIG_REGULATOR_S2MPS11 is not set ++# CONFIG_REGULATOR_S5M8767 is not set ++# CONFIG_REGULATOR_SLG51000 is not set ++# CONFIG_REGULATOR_SY8106A is not set ++# CONFIG_REGULATOR_SY8824X is not set ++# CONFIG_REGULATOR_SY8827N is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set + CONFIG_REGULATOR_TPS65132=y ++# CONFIG_REGULATOR_TPS6524X is not set ++# CONFIG_REGULATOR_TPS61280 is not set ++# CONFIG_REGULATOR_VCTRL is not set ++# CONFIG_REGULATOR_VEXPRESS is not set ++# CONFIG_REGULATOR_QCOM_LABIBB is not set + CONFIG_REGULATOR_PMIC_OTP=y + CONFIG_REGULATOR_NCP81599=y + CONFIG_RC_CORE=y ++CONFIG_RC_MAP=y ++# CONFIG_LIRC is not set ++# CONFIG_RC_DECODERS is not set ++# CONFIG_RC_DEVICES is not set ++# CONFIG_MEDIA_CEC_SUPPORT is not set + CONFIG_MEDIA_SUPPORT=y + CONFIG_MEDIA_SUPPORT_FILTER=y + # CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set ++ ++# ++# Media device types ++# + CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set + CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set + CONFIG_MEDIA_PLATFORM_SUPPORT=y + CONFIG_MEDIA_TEST_SUPPORT=y ++# end of Media device types ++ ++CONFIG_VIDEO_DEV=y ++CONFIG_MEDIA_CONTROLLER=y ++CONFIG_DVB_CORE=y ++ ++# ++# Video4Linux options ++# ++CONFIG_VIDEO_V4L2=y ++CONFIG_VIDEO_V4L2_I2C=y + CONFIG_VIDEO_V4L2_SUBDEV_API=y ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_V4L2_FWNODE=y ++CONFIG_VIDEOBUF_GEN=m ++CONFIG_VIDEOBUF_VMALLOC=m ++# end of Video4Linux options ++ ++# ++# Media controller options ++# ++# CONFIG_MEDIA_CONTROLLER_DVB is not set ++# end of Media controller options ++ ++# ++# Digital TV options ++# ++# CONFIG_DVB_MMAP is not set ++CONFIG_DVB_NET=y ++CONFIG_DVB_MAX_ADAPTERS=16 + # CONFIG_DVB_DYNAMIC_MINORS is not set ++# CONFIG_DVB_DEMUX_SECTION_LOSS_LOG is not set ++# CONFIG_DVB_ULE_DEBUG is not set ++# end of Digital TV options ++ ++# ++# Media drivers ++# ++ ++# ++# Drivers filtered as selected at 'Filter media drivers' ++# + CONFIG_MEDIA_USB_SUPPORT=y ++ ++# ++# Webcam devices ++# + CONFIG_USB_VIDEO_CLASS=m ++CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y ++CONFIG_USB_GSPCA=m + CONFIG_USB_M5602=m + CONFIG_USB_STV06XX=m + CONFIG_USB_GL860=m +@@ -872,20 +4505,265 @@ CONFIG_USB_GSPCA_VICAM=m + CONFIG_USB_GSPCA_XIRLINK_CIT=m + CONFIG_USB_GSPCA_ZC3XX=m + CONFIG_USB_PWC=m ++# CONFIG_USB_PWC_DEBUG is not set ++CONFIG_USB_PWC_INPUT_EVDEV=y + CONFIG_VIDEO_CPIA2=m + CONFIG_USB_ZR364XX=m + CONFIG_USB_STKWEBCAM=m + CONFIG_USB_S2255=m + CONFIG_VIDEO_USBTV=m ++ ++# ++# Analog/digital TV USB devices ++# ++# CONFIG_VIDEO_AU0828 is not set ++# CONFIG_VIDEO_CX231XX is not set ++# CONFIG_VIDEO_TM6000 is not set ++ ++# ++# Digital TV USB devices ++# ++# CONFIG_DVB_USB is not set ++# CONFIG_DVB_USB_V2 is not set ++# CONFIG_DVB_TTUSB_BUDGET is not set ++# CONFIG_DVB_TTUSB_DEC is not set ++# CONFIG_SMS_USB_DRV is not set ++# CONFIG_DVB_B2C2_FLEXCOP_USB is not set ++# CONFIG_DVB_AS102 is not set ++ ++# ++# Webcam, TV (analog/digital) USB devices ++# ++# CONFIG_VIDEO_EM28XX is not set ++# CONFIG_MEDIA_PCI_SUPPORT is not set ++CONFIG_VIDEOBUF2_CORE=y ++CONFIG_VIDEOBUF2_V4L2=y ++CONFIG_VIDEOBUF2_MEMOPS=y ++CONFIG_VIDEOBUF2_DMA_CONTIG=y ++CONFIG_VIDEOBUF2_VMALLOC=m + CONFIG_V4L_PLATFORM_DRIVERS=y ++# CONFIG_VIDEO_CAFE_CCIC is not set ++# CONFIG_VIDEO_CADENCE is not set ++# CONFIG_VIDEO_ASPEED is not set ++# CONFIG_VIDEO_MUX is not set ++# CONFIG_VIDEO_XILINX is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_DVB_PLATFORM_DRIVERS is not set ++ ++# ++# NVIDIA overlay V4L platform devices ++# ++CONFIG_TEGRA_MIPI_CAL=y + CONFIG_VIDEO_CAMERA=y ++CONFIG_VIDEO_TEGRA_VI=y ++CONFIG_VIDEO_TEGRA_VI_TPG=m ++CONFIG_VIDEO_CAMERA_SKT=m + CONFIG_VIDEO_ISC=m + CONFIG_VIDEO_CDI=m ++# CONFIG_VIDEO_TEGRA_VIVID is not set ++# end of NVIDIA overlay V4L platform devices ++ ++# ++# MMC/SDIO DVB adapters ++# ++# CONFIG_SMS_SDIO_DRV is not set + CONFIG_V4L_TEST_DRIVERS=y +-CONFIG_VIDEO_ECAM=m ++# CONFIG_VIDEO_VIMC is not set ++# CONFIG_VIDEO_VIVID is not set ++# CONFIG_VIDEO_VIM2M is not set ++# CONFIG_VIDEO_VICODEC is not set ++# CONFIG_DVB_TEST_DRIVERS is not set ++# end of Media drivers ++ ++# ++# Media ancillary drivers ++# ++CONFIG_MEDIA_ATTACH=y ++CONFIG_VIDEO_IR_I2C=y ++ ++# ++# Audio decoders, processors and mixers ++# ++# CONFIG_VIDEO_TVAUDIO is not set ++# CONFIG_VIDEO_TDA7432 is not set ++# CONFIG_VIDEO_TDA9840 is not set ++# CONFIG_VIDEO_TDA1997X is not set ++# CONFIG_VIDEO_TEA6415C is not set ++# CONFIG_VIDEO_TEA6420 is not set ++# CONFIG_VIDEO_MSP3400 is not set ++# CONFIG_VIDEO_CS3308 is not set ++# CONFIG_VIDEO_CS5345 is not set ++# CONFIG_VIDEO_CS53L32A is not set ++# CONFIG_VIDEO_TLV320AIC23B is not set ++# CONFIG_VIDEO_UDA1342 is not set ++# CONFIG_VIDEO_WM8775 is not set ++# CONFIG_VIDEO_WM8739 is not set ++# CONFIG_VIDEO_VP27SMPX is not set ++# CONFIG_VIDEO_SONY_BTF_MPX is not set ++# end of Audio decoders, processors and mixers ++ ++# ++# RDS decoders ++# ++# CONFIG_VIDEO_SAA6588 is not set ++# end of RDS decoders ++ ++# ++# Video decoders ++# ++# CONFIG_VIDEO_ADV7180 is not set ++# CONFIG_VIDEO_ADV7183 is not set ++# CONFIG_VIDEO_ADV748X is not set ++# CONFIG_VIDEO_ADV7604 is not set ++# CONFIG_VIDEO_ADV7842 is not set ++# CONFIG_VIDEO_BT819 is not set ++# CONFIG_VIDEO_BT856 is not set ++# CONFIG_VIDEO_BT866 is not set ++# CONFIG_VIDEO_KS0127 is not set ++# CONFIG_VIDEO_ML86V7667 is not set ++# CONFIG_VIDEO_SAA7110 is not set ++# CONFIG_VIDEO_SAA711X is not set ++# CONFIG_VIDEO_TC358743 is not set ++# CONFIG_VIDEO_TVP514X is not set ++# CONFIG_VIDEO_TVP5150 is not set ++# CONFIG_VIDEO_TVP7002 is not set ++# CONFIG_VIDEO_TW2804 is not set ++# CONFIG_VIDEO_TW9903 is not set ++# CONFIG_VIDEO_TW9906 is not set ++# CONFIG_VIDEO_TW9910 is not set ++# CONFIG_VIDEO_VPX3220 is not set ++# CONFIG_VIDEO_MAX9286 is not set ++ ++# ++# Video and audio decoders ++# ++# CONFIG_VIDEO_SAA717X is not set ++# CONFIG_VIDEO_CX25840 is not set ++# end of Video decoders ++ ++# ++# Video encoders ++# ++# CONFIG_VIDEO_SAA7127 is not set ++# CONFIG_VIDEO_SAA7185 is not set ++# CONFIG_VIDEO_ADV7170 is not set ++# CONFIG_VIDEO_ADV7175 is not set ++# CONFIG_VIDEO_ADV7343 is not set ++# CONFIG_VIDEO_ADV7393 is not set ++# CONFIG_VIDEO_ADV7511 is not set ++# CONFIG_VIDEO_AD9389B is not set ++# CONFIG_VIDEO_AK881X is not set ++# CONFIG_VIDEO_THS8200 is not set ++# end of Video encoders ++ ++# ++# Video improvement chips ++# ++# CONFIG_VIDEO_UPD64031A is not set ++# CONFIG_VIDEO_UPD64083 is not set ++# end of Video improvement chips ++ ++# ++# Audio/Video compression chips ++# ++# CONFIG_VIDEO_SAA6752HS is not set ++# end of Audio/Video compression chips ++ ++# ++# SDR tuner chips ++# ++# end of SDR tuner chips ++ ++# ++# Miscellaneous helper chips ++# ++# CONFIG_VIDEO_THS7303 is not set ++# CONFIG_VIDEO_M52790 is not set ++# CONFIG_VIDEO_I2C is not set ++# CONFIG_VIDEO_ST_MIPID02 is not set ++# end of Miscellaneous helper chips ++ ++# ++# Camera sensor devices ++# ++# CONFIG_VIDEO_HI556 is not set ++# CONFIG_VIDEO_IMX214 is not set ++# CONFIG_VIDEO_IMX219 is not set ++# CONFIG_VIDEO_IMX258 is not set ++# CONFIG_VIDEO_IMX274 is not set ++# CONFIG_VIDEO_IMX290 is not set ++# CONFIG_VIDEO_IMX319 is not set ++# CONFIG_VIDEO_IMX355 is not set ++# CONFIG_VIDEO_OV2640 is not set ++# CONFIG_VIDEO_OV2659 is not set ++# CONFIG_VIDEO_OV2680 is not set ++# CONFIG_VIDEO_OV2685 is not set ++# CONFIG_VIDEO_OV2740 is not set ++# CONFIG_VIDEO_OV5640 is not set ++# CONFIG_VIDEO_OV5645 is not set ++# CONFIG_VIDEO_OV5647 is not set ++# CONFIG_VIDEO_OV6650 is not set ++# CONFIG_VIDEO_OV5670 is not set ++# CONFIG_VIDEO_OV5675 is not set ++# CONFIG_VIDEO_OV5695 is not set ++# CONFIG_VIDEO_OV7251 is not set ++# CONFIG_VIDEO_OV772X is not set ++# CONFIG_VIDEO_OV7640 is not set ++# CONFIG_VIDEO_OV7670 is not set ++# CONFIG_VIDEO_OV7740 is not set ++# CONFIG_VIDEO_OV8856 is not set ++# CONFIG_VIDEO_OV9640 is not set ++# CONFIG_VIDEO_OV9650 is not set ++# CONFIG_VIDEO_OV13858 is not set ++# CONFIG_VIDEO_VS6624 is not set ++# CONFIG_VIDEO_MT9M001 is not set ++# CONFIG_VIDEO_MT9M032 is not set ++# CONFIG_VIDEO_MT9M111 is not set ++# CONFIG_VIDEO_MT9P031 is not set ++# CONFIG_VIDEO_MT9T001 is not set ++# CONFIG_VIDEO_MT9T112 is not set ++# CONFIG_VIDEO_MT9V011 is not set ++# CONFIG_VIDEO_MT9V032 is not set ++# CONFIG_VIDEO_MT9V111 is not set ++# CONFIG_VIDEO_SR030PC30 is not set ++# CONFIG_VIDEO_NOON010PC30 is not set ++# CONFIG_VIDEO_M5MOLS is not set ++# CONFIG_VIDEO_RDACM20 is not set ++# CONFIG_VIDEO_RJ54N1 is not set ++# CONFIG_VIDEO_S5K6AA is not set ++# CONFIG_VIDEO_S5K6A3 is not set ++# CONFIG_VIDEO_S5K4ECGX is not set ++# CONFIG_VIDEO_S5K5BAF is not set ++# CONFIG_VIDEO_SMIAPP is not set ++# CONFIG_VIDEO_ET8EK8 is not set ++# CONFIG_VIDEO_S5C73M3 is not set ++# end of Camera sensor devices ++ ++# ++# Lens drivers ++# ++# CONFIG_VIDEO_AD5820 is not set ++# CONFIG_VIDEO_AK7375 is not set ++# CONFIG_VIDEO_DW9714 is not set ++# CONFIG_VIDEO_DW9768 is not set ++# CONFIG_VIDEO_DW9807_VCM is not set ++# end of Lens drivers ++ ++# ++# Flash devices ++# ++# CONFIG_VIDEO_ADP1653 is not set ++# CONFIG_VIDEO_LM3560 is not set ++# CONFIG_VIDEO_LM3646 is not set ++# end of Flash devices ++ ++# ++# NVIDIA overlay Encoders, decoders, sensors and other helper chips ++# + CONFIG_NV_VIDEO_IMX185=m +-CONFIG_NV_VIDEO_IMX219=m + CONFIG_NV_VIDEO_IMX477=m ++CONFIG_VIDEO_ECAM=m ++CONFIG_NV_VIDEO_IMX219=m + CONFIG_NV_VIDEO_IMX268=m + CONFIG_NV_VIDEO_IMX274=m + CONFIG_NV_VIDEO_IMX318=m +@@ -903,16 +4781,55 @@ CONFIG_NV_VIDEO_IMX390=y + CONFIG_NV_DESER_MAX96712=m + CONFIG_NV_VIDEO_AR0234=m + CONFIG_NV_VIDEO_HAWK_OWL=m ++# end of NVIDIA overlay Encoders, decoders, sensors and other helper chips ++ ++# ++# SPI helper chips ++# ++# CONFIG_VIDEO_GS1662 is not set ++# end of SPI helper chips ++ ++# ++# Media SPI Adapters ++# + # CONFIG_CXD2880_SPI_DRV is not set ++# end of Media SPI Adapters ++ ++# CONFIG_VIDEO_IMX204 is not set ++CONFIG_MEDIA_TUNER=y ++ ++# ++# Customize TV tuners ++# + CONFIG_MEDIA_TUNER_SIMPLE=y + # CONFIG_MEDIA_TUNER_TDA18250 is not set + CONFIG_MEDIA_TUNER_TDA8290=y ++CONFIG_MEDIA_TUNER_TDA827X=y ++CONFIG_MEDIA_TUNER_TDA18271=y + # CONFIG_MEDIA_TUNER_TDA18272 is not set ++CONFIG_MEDIA_TUNER_TDA9887=y ++CONFIG_MEDIA_TUNER_TEA5761=m ++CONFIG_MEDIA_TUNER_TEA5767=m ++CONFIG_MEDIA_TUNER_MSI001=m + CONFIG_MEDIA_TUNER_MT20XX=y ++CONFIG_MEDIA_TUNER_MT2060=m ++CONFIG_MEDIA_TUNER_MT2063=m ++CONFIG_MEDIA_TUNER_MT2266=m ++CONFIG_MEDIA_TUNER_MT2131=m ++CONFIG_MEDIA_TUNER_QT1010=m + CONFIG_MEDIA_TUNER_XC2028=y + CONFIG_MEDIA_TUNER_XC5000=y + CONFIG_MEDIA_TUNER_XC4000=y ++CONFIG_MEDIA_TUNER_MXL5005S=m ++CONFIG_MEDIA_TUNER_MXL5007T=m + CONFIG_MEDIA_TUNER_MC44S803=y ++CONFIG_MEDIA_TUNER_MAX2165=m ++CONFIG_MEDIA_TUNER_TDA18218=m ++CONFIG_MEDIA_TUNER_FC0011=m ++CONFIG_MEDIA_TUNER_FC0012=m ++CONFIG_MEDIA_TUNER_FC0013=m ++CONFIG_MEDIA_TUNER_TDA18212=m ++CONFIG_MEDIA_TUNER_E4000=m + # CONFIG_MEDIA_TUNER_FC2580 is not set + # CONFIG_MEDIA_TUNER_M88RS6000T is not set + # CONFIG_MEDIA_TUNER_TUA9001 is not set +@@ -922,51 +4839,716 @@ CONFIG_MEDIA_TUNER_MC44S803=y + # CONFIG_MEDIA_TUNER_MXL301RF is not set + # CONFIG_MEDIA_TUNER_QM1D1C0042 is not set + # CONFIG_MEDIA_TUNER_QM1D1B0004 is not set ++# end of Customize TV tuners ++ ++# ++# Nvidia overlay Customize TV tuners ++# ++# end of Nvidia overlay Customize TV tuners ++ ++# ++# Customise DVB Frontends ++# ++ ++# ++# Multistandard (satellite) frontends ++# ++CONFIG_DVB_STB0899=m ++CONFIG_DVB_STB6100=m ++CONFIG_DVB_STV090x=m ++CONFIG_DVB_STV0910=m ++CONFIG_DVB_STV6110x=m ++CONFIG_DVB_STV6111=m ++CONFIG_DVB_MXL5XX=m ++CONFIG_DVB_M88DS3103=m ++ ++# ++# Multistandard (cable + terrestrial) frontends ++# ++CONFIG_DVB_DRXK=m ++CONFIG_DVB_TDA18271C2DD=m ++CONFIG_DVB_SI2165=m ++CONFIG_DVB_MN88472=m ++CONFIG_DVB_MN88473=m ++ ++# ++# DVB-S (satellite) frontends ++# ++CONFIG_DVB_CX24110=m ++CONFIG_DVB_CX24123=m ++CONFIG_DVB_MT312=m ++CONFIG_DVB_ZL10036=m ++CONFIG_DVB_ZL10039=m ++CONFIG_DVB_S5H1420=m ++CONFIG_DVB_STV0288=m ++CONFIG_DVB_STB6000=m ++CONFIG_DVB_STV0299=m ++CONFIG_DVB_STV6110=m ++CONFIG_DVB_STV0900=m ++CONFIG_DVB_TDA8083=m ++CONFIG_DVB_TDA10086=m ++CONFIG_DVB_TDA8261=m ++CONFIG_DVB_VES1X93=m ++CONFIG_DVB_TUNER_ITD1000=m ++CONFIG_DVB_TUNER_CX24113=m ++CONFIG_DVB_TDA826X=m ++CONFIG_DVB_TUA6100=m ++CONFIG_DVB_CX24116=m ++CONFIG_DVB_CX24117=m ++CONFIG_DVB_CX24120=m ++CONFIG_DVB_SI21XX=m ++CONFIG_DVB_TS2020=m ++CONFIG_DVB_DS3000=m ++CONFIG_DVB_MB86A16=m ++CONFIG_DVB_TDA10071=m ++ ++# ++# DVB-T (terrestrial) frontends ++# ++CONFIG_DVB_SP8870=m ++CONFIG_DVB_SP887X=m ++CONFIG_DVB_CX22700=m ++CONFIG_DVB_CX22702=m ++CONFIG_DVB_S5H1432=m ++CONFIG_DVB_DRXD=m ++CONFIG_DVB_L64781=m ++CONFIG_DVB_TDA1004X=m ++CONFIG_DVB_NXT6000=m ++CONFIG_DVB_MT352=m ++CONFIG_DVB_ZL10353=m ++CONFIG_DVB_DIB3000MB=m ++CONFIG_DVB_DIB3000MC=m ++CONFIG_DVB_DIB7000M=m ++CONFIG_DVB_DIB7000P=m ++CONFIG_DVB_DIB9000=m ++CONFIG_DVB_TDA10048=m ++CONFIG_DVB_AF9013=m ++CONFIG_DVB_EC100=m ++CONFIG_DVB_STV0367=m ++CONFIG_DVB_CXD2820R=m ++CONFIG_DVB_CXD2841ER=m ++CONFIG_DVB_RTL2830=m ++CONFIG_DVB_RTL2832=m ++CONFIG_DVB_SI2168=m ++CONFIG_DVB_ZD1301_DEMOD=m + # CONFIG_DVB_CXD2880 is not set ++ ++# ++# DVB-C (cable) frontends ++# ++CONFIG_DVB_VES1820=m ++CONFIG_DVB_TDA10021=m ++CONFIG_DVB_TDA10023=m ++CONFIG_DVB_STV0297=m ++ ++# ++# ATSC (North American/Korean Terrestrial/Cable DTV) frontends ++# ++CONFIG_DVB_NXT200X=m ++CONFIG_DVB_OR51211=m ++CONFIG_DVB_OR51132=m ++CONFIG_DVB_BCM3510=m ++CONFIG_DVB_LGDT330X=m ++CONFIG_DVB_LGDT3305=m ++CONFIG_DVB_LGDT3306A=m ++CONFIG_DVB_LG2160=m ++CONFIG_DVB_S5H1409=m ++CONFIG_DVB_AU8522=m ++CONFIG_DVB_AU8522_DTV=m ++CONFIG_DVB_AU8522_V4L=m ++CONFIG_DVB_S5H1411=m ++ ++# ++# ISDB-T (terrestrial) frontends ++# ++CONFIG_DVB_S921=m ++CONFIG_DVB_DIB8000=m ++CONFIG_DVB_MB86A20S=m ++ ++# ++# ISDB-S (satellite) & ISDB-T (terrestrial) frontends ++# ++CONFIG_DVB_TC90522=m + # CONFIG_DVB_MN88443X is not set ++ ++# ++# Digital terrestrial only tuners/PLL ++# ++CONFIG_DVB_PLL=m ++CONFIG_DVB_TUNER_DIB0070=m ++CONFIG_DVB_TUNER_DIB0090=m ++ ++# ++# SEC control devices for DVB-S ++# ++CONFIG_DVB_DRX39XYJ=m ++CONFIG_DVB_LNBH25=m + # CONFIG_DVB_LNBH29 is not set ++CONFIG_DVB_LNBP21=m ++CONFIG_DVB_LNBP22=m ++CONFIG_DVB_ISL6405=m ++CONFIG_DVB_ISL6421=m ++CONFIG_DVB_ISL6423=m ++CONFIG_DVB_A8293=m ++CONFIG_DVB_LGS8GL5=m ++CONFIG_DVB_LGS8GXX=m ++CONFIG_DVB_ATBM8830=m ++CONFIG_DVB_TDA665x=m ++CONFIG_DVB_IX2505V=m ++CONFIG_DVB_M88RS2000=m ++CONFIG_DVB_AF9033=m ++CONFIG_DVB_HORUS3A=m ++CONFIG_DVB_ASCOT2E=m ++CONFIG_DVB_HELENE=m ++ ++# ++# Common Interface (EN50221) controller drivers ++# + # CONFIG_DVB_CXD2099 is not set ++CONFIG_DVB_SP2=m ++# end of Customise DVB Frontends ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++# end of Media ancillary drivers ++ ++# ++# Graphics support ++# + # CONFIG_VGA_ARB is not set ++# CONFIG_TEGRA_HOST1X is not set + CONFIG_DRM=y ++CONFIG_DRM_MIPI_DSI=y ++# CONFIG_DRM_DP_AUX_CHARDEV is not set ++# CONFIG_DRM_DEBUG_MM is not set ++# CONFIG_DRM_DEBUG_SELFTEST is not set ++CONFIG_DRM_KMS_HELPER=y ++CONFIG_DRM_KMS_FB_HELPER=y ++# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set ++CONFIG_DRM_FBDEV_EMULATION=y ++CONFIG_DRM_FBDEV_OVERALLOC=100 ++# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set ++# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set ++# CONFIG_DRM_DP_CEC is not set ++CONFIG_DRM_TTM=m ++CONFIG_DRM_TTM_DMA_PAGE_POOL=y ++CONFIG_DRM_VRAM_HELPER=m ++CONFIG_DRM_TTM_HELPER=m ++CONFIG_DRM_GEM_CMA_HELPER=y ++CONFIG_DRM_KMS_CMA_HELPER=y ++ ++# ++# I2C encoder or helper chips ++# ++# CONFIG_DRM_I2C_CH7006 is not set ++# CONFIG_DRM_I2C_SIL164 is not set + CONFIG_DRM_I2C_NXP_TDA998X=m ++# CONFIG_DRM_I2C_NXP_TDA9950 is not set ++# end of I2C encoder or helper chips ++ ++# ++# ARM devices ++# ++# CONFIG_DRM_HDLCD is not set ++# CONFIG_DRM_MALI_DISPLAY is not set ++# CONFIG_DRM_KOMEDA is not set ++# end of ARM devices ++ ++# CONFIG_DRM_RADEON is not set ++# CONFIG_DRM_AMDGPU is not set ++# CONFIG_DRM_NOUVEAU is not set ++# CONFIG_DRM_VGEM is not set ++# CONFIG_DRM_VKMS is not set ++# CONFIG_DRM_UDL is not set ++# CONFIG_DRM_AST is not set ++# CONFIG_DRM_MGAG200 is not set + CONFIG_DRM_RCAR_DW_HDMI=m + CONFIG_DRM_RCAR_LVDS=m ++# CONFIG_DRM_QXL is not set ++# CONFIG_DRM_BOCHS is not set ++# CONFIG_DRM_VIRTIO_GPU is not set ++# CONFIG_DRM_TEGRA is not set ++CONFIG_DRM_PANEL=y ++ ++# ++# Display Panels ++# ++# CONFIG_DRM_PANEL_ARM_VERSATILE is not set ++# CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 is not set ++# CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set ++# CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set + CONFIG_DRM_PANEL_LVDS=m + CONFIG_DRM_PANEL_SIMPLE=m ++# CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set ++# CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set ++# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set ++# CONFIG_DRM_PANEL_ILITEK_IL9322 is not set ++# CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set ++# CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set ++# CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set ++# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set ++# CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W is not set ++# CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829 is not set ++# CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set ++# CONFIG_DRM_PANEL_LG_LB035Q02 is not set ++# CONFIG_DRM_PANEL_LG_LG4573 is not set ++# CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set ++# CONFIG_DRM_PANEL_NOVATEK_NT35510 is not set ++# CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set ++# CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set ++# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set ++# CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set ++# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set ++# CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set ++# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set ++# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set ++# CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set ++# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set ++# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set ++# CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set ++# CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set ++# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set ++# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set ++# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set ++# CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set ++# CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set ++# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set ++# CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set ++# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set ++# CONFIG_DRM_PANEL_SITRONIX_ST7703 is not set ++# CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set ++# CONFIG_DRM_PANEL_SONY_ACX424AKP is not set ++# CONFIG_DRM_PANEL_SONY_ACX565AKM is not set ++# CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set ++# CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set ++# CONFIG_DRM_PANEL_TPO_TPG110 is not set ++# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set ++# CONFIG_DRM_PANEL_VISIONOX_RM69299 is not set ++# CONFIG_DRM_PANEL_XINPENG_XPP055C272 is not set ++# end of Display Panels ++ ++CONFIG_DRM_BRIDGE=y ++CONFIG_DRM_PANEL_BRIDGE=y ++ ++# ++# Display Interface Bridges ++# ++# CONFIG_DRM_CDNS_DSI is not set ++# CONFIG_DRM_CHRONTEL_CH7033 is not set ++# CONFIG_DRM_DISPLAY_CONNECTOR is not set ++# CONFIG_DRM_LONTIUM_LT9611 is not set ++# CONFIG_DRM_LVDS_CODEC is not set ++# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set ++# CONFIG_DRM_NWL_MIPI_DSI is not set ++# CONFIG_DRM_NXP_PTN3460 is not set ++# CONFIG_DRM_PARADE_PS8622 is not set ++# CONFIG_DRM_PARADE_PS8640 is not set ++# CONFIG_DRM_SIL_SII8620 is not set + CONFIG_DRM_SII902X=m ++# CONFIG_DRM_SII9234 is not set ++# CONFIG_DRM_SIMPLE_BRIDGE is not set ++# CONFIG_DRM_THINE_THC63LVD1024 is not set ++# CONFIG_DRM_TOSHIBA_TC358762 is not set ++# CONFIG_DRM_TOSHIBA_TC358764 is not set ++# CONFIG_DRM_TOSHIBA_TC358767 is not set ++# CONFIG_DRM_TOSHIBA_TC358768 is not set ++# CONFIG_DRM_TOSHIBA_TC358775 is not set ++# CONFIG_DRM_TI_TFP410 is not set ++# CONFIG_DRM_TI_SN65DSI86 is not set ++# CONFIG_DRM_TI_TPD12S015 is not set ++# CONFIG_DRM_ANALOGIX_ANX6345 is not set ++# CONFIG_DRM_ANALOGIX_ANX78XX is not set ++# CONFIG_DRM_I2C_ADV7511 is not set ++# CONFIG_DRM_CDNS_MHDP8546 is not set ++CONFIG_DRM_DW_HDMI=m ++# CONFIG_DRM_DW_HDMI_AHB_AUDIO is not set ++# CONFIG_DRM_DW_HDMI_I2S_AUDIO is not set ++# CONFIG_DRM_DW_HDMI_CEC is not set ++# end of Display Interface Bridges ++ ++# CONFIG_DRM_ETNAVIV is not set ++# CONFIG_DRM_ARCPGU is not set + CONFIG_DRM_HISI_HIBMC=m + CONFIG_DRM_HISI_KIRIN=m ++# CONFIG_DRM_MXSFB is not set ++# CONFIG_DRM_CIRRUS_QEMU is not set ++# CONFIG_DRM_GM12U320 is not set ++# CONFIG_TINYDRM_HX8357D is not set ++# CONFIG_TINYDRM_ILI9225 is not set ++# CONFIG_TINYDRM_ILI9341 is not set ++# CONFIG_TINYDRM_ILI9486 is not set ++# CONFIG_TINYDRM_MI0283QT is not set ++# CONFIG_TINYDRM_REPAPER is not set ++# CONFIG_TINYDRM_ST7586 is not set ++# CONFIG_TINYDRM_ST7735R is not set ++# CONFIG_DRM_PL111 is not set ++# CONFIG_DRM_LIMA is not set ++# CONFIG_DRM_PANFROST is not set ++# CONFIG_DRM_TIDSS is not set ++# CONFIG_DRM_LEGACY is not set ++CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y + CONFIG_DRM_TEGRA_UDRM=m ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB_CMDLINE=y ++CONFIG_FB_NOTIFY=y ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CFB_FILLRECT=y ++CONFIG_FB_CFB_COPYAREA=y ++CONFIG_FB_CFB_IMAGEBLIT=y ++CONFIG_FB_SYS_FILLRECT=y ++CONFIG_FB_SYS_COPYAREA=y ++CONFIG_FB_SYS_IMAGEBLIT=y ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++CONFIG_FB_SYS_FOPS=y ++CONFIG_FB_DEFERRED_IO=y ++CONFIG_FB_MODE_HELPERS=y + CONFIG_FB_MODE_PIXCLOCK_HZ=y ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_CIRRUS is not set ++# CONFIG_FB_PM2 is not set ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_CYBER2000 is not set ++# CONFIG_FB_ASILIANT is not set ++# CONFIG_FB_IMSTT is not set ++CONFIG_FB_EFI=y ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_NVIDIA is not set ++# CONFIG_FB_RIVA is not set ++# CONFIG_FB_I740 is not set ++# CONFIG_FB_MATROX is not set ++# CONFIG_FB_RADEON is not set ++# CONFIG_FB_ATY128 is not set ++# CONFIG_FB_ATY is not set ++# CONFIG_FB_S3 is not set ++# CONFIG_FB_SAVAGE is not set ++# CONFIG_FB_SIS is not set ++# CONFIG_FB_NEOMAGIC is not set ++# CONFIG_FB_KYRO is not set ++# CONFIG_FB_3DFX is not set ++# CONFIG_FB_VOODOO1 is not set ++# CONFIG_FB_VT8623 is not set ++# CONFIG_FB_TRIDENT is not set ++# CONFIG_FB_ARK is not set ++# CONFIG_FB_PM3 is not set ++# CONFIG_FB_CARMINE is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_IBM_GXT4500 is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_MB862XX is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_FB_SM712 is not set ++# end of Frame buffer Devices ++ ++# ++# Backlight & LCD device support ++# + CONFIG_LCD_CLASS_DEVICE=m ++# CONFIG_LCD_L4F00242T03 is not set ++# CONFIG_LCD_LMS283GF05 is not set ++# CONFIG_LCD_LTV350QV is not set ++# CONFIG_LCD_ILI922X is not set ++# CONFIG_LCD_ILI9320 is not set ++# CONFIG_LCD_TDO24M is not set ++# CONFIG_LCD_VGG2432A4 is not set ++# CONFIG_LCD_PLATFORM is not set ++# CONFIG_LCD_AMS369FG06 is not set ++# CONFIG_LCD_LMS501KF03 is not set ++# CONFIG_LCD_HX8357 is not set ++# CONFIG_LCD_OTM3225A is not set ++CONFIG_BACKLIGHT_CLASS_DEVICE=y ++# CONFIG_BACKLIGHT_KTD253 is not set + CONFIG_BACKLIGHT_PWM=y ++# CONFIG_BACKLIGHT_QCOM_WLED is not set ++# CONFIG_BACKLIGHT_ADP8860 is not set ++# CONFIG_BACKLIGHT_ADP8870 is not set ++# CONFIG_BACKLIGHT_LM3630A is not set ++# CONFIG_BACKLIGHT_LM3639 is not set + CONFIG_BACKLIGHT_LP855X=y ++# CONFIG_BACKLIGHT_GPIO is not set ++# CONFIG_BACKLIGHT_LV5207LP is not set ++# CONFIG_BACKLIGHT_BD6107 is not set ++# CONFIG_BACKLIGHT_ARCXCNN is not set ++# CONFIG_BACKLIGHT_LED is not set ++# end of Backlight & LCD device support ++ + CONFIG_TEGRA_GRHOST=y ++CONFIG_TEGRA_GRHOST_ISP=y ++CONFIG_TEGRA_GRHOST_VIC=y ++CONFIG_TEGRA_GRHOST_NVDEC=y ++CONFIG_TEGRA_GRHOST_NVDEC_SECURE=y ++CONFIG_TEGRA_GRHOST_NVENC=y ++CONFIG_TEGRA_GRHOST_NVJPG=y ++CONFIG_TEGRA_GRHOST_OFA=y ++CONFIG_TEGRA_GRHOST_TSEC=y + CONFIG_TEGRA_GRHOST_NVCSI=y ++CONFIG_TEGRA_GRHOST_SCALE=y ++CONFIG_TEGRA_GRHOST_DEFAULT_TIMEOUT=10000 ++CONFIG_TEGRA_GRHOST_SYNC=y ++CONFIG_TEGRA_GRHOST_VHOST=y + CONFIG_TEGRA_GR_VIRTUALIZATION=y ++# CONFIG_NVDEC_BOOTLOADER is not set ++CONFIG_TEGRA_CAMERA_PLATFORM=y ++ ++# ++# NVIDIA Tegra Display Driver options ++# + CONFIG_TEGRA_DC=y ++# CONFIG_TEGRA_NVDISPLAY is not set ++CONFIG_TEGRA_DC_64BIT_SUPPORT=y ++CONFIG_TEGRA_DC_TEMPORAL_DITHER=y ++CONFIG_FB_TEGRA=y + CONFIG_TEGRA_DC_SCREEN_CAPTURE=y + CONFIG_TEGRA_DSI=y ++# CONFIG_TEGRA_DSI2EDP_TC358767 is not set ++# CONFIG_TEGRA_DSI2EDP_SN65DSI86 is not set ++# CONFIG_TEGRA_DSI2LVDS_SN65DSI85 is not set ++# CONFIG_TEGRA_LVDS2FPDL_DS90UB947 is not set ++# CONFIG_TEGRA_DS90UH948Q_DESER is not set + CONFIG_MAXIM_GMSL_DP_SERIALIZER=y ++# CONFIG_TEGRA_EDP2LVDS_PS8625 is not set ++CONFIG_TEGRA_DP=y + CONFIG_TEGRA_HDMI2_0=y ++# CONFIG_TEGRA_HDMI2GMSL_MAX929x is not set ++# CONFIG_TEGRA_HDMI2DSI_TC358870 is not set ++CONFIG_TEGRA_HDA_DC=y ++# CONFIG_TEGRA_HDMI2FPD_DS90UH949 is not set ++# CONFIG_TEGRA_NVSR is not set ++# CONFIG_TEGRA_VRR is not set ++# CONFIG_TEGRA_HDMIVRR is not set + CONFIG_TEGRA_HDMIHDCP=y ++# CONFIG_TEGRA_DEBUG_HDCP is not set + CONFIG_TEGRA_DPHDCP=y ++# CONFIG_TEGRA_DEBUG_DP_HDCP is not set ++# CONFIG_TEGRA_YUV_BYPASS_MODE_FILTER is not set ++CONFIG_TEGRA_DC_FAKE_PANEL_SUPPORT=y + CONFIG_TEGRA_CEC_SUPPORT=y ++CONFIG_TEGRA_T23X_GRHOST=y ++CONFIG_TEGRA_T23X_GRHOST_PVA=y ++CONFIG_TEGRA_GRHOST_NVDLA=y ++CONFIG_TEGRA_T19x_GRHOST_PVA=y ++CONFIG_TEGRA_GRHOST_PVA=y + CONFIG_TEGRA_GRHOST_SLVSEC=y ++CONFIG_TEGRA_GRHOST_CAPTURE_SUPPORT=y ++# CONFIG_TEGRA_GRHOST_LEGACY_PD is not set ++# CONFIG_TEGRA_PVA_V1 is not set ++CONFIG_VIDEOMODE_HELPERS=y ++CONFIG_HDMI=y ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++CONFIG_DUMMY_CONSOLE_COLUMNS=80 ++CONFIG_DUMMY_CONSOLE_ROWS=25 + CONFIG_FRAMEBUFFER_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set ++CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y ++# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set ++# end of Console display driver support ++ + CONFIG_LOGO=y + # CONFIG_LOGO_LINUX_MONO is not set + # CONFIG_LOGO_LINUX_VGA16 is not set + # CONFIG_LOGO_LINUX_CLUT224 is not set ++ ++# ++# NVIDIA Tegra Display Driver options ++# ++# end of Graphics support ++ + CONFIG_SOUND=y + CONFIG_SND=y ++CONFIG_SND_TIMER=y ++CONFIG_SND_PCM=y ++CONFIG_SND_PCM_ELD=y ++CONFIG_SND_PCM_IEC958=y ++CONFIG_SND_DMAENGINE_PCM=y ++CONFIG_SND_HWDEP=y ++CONFIG_SND_RAWMIDI=y ++CONFIG_SND_COMPRESS_OFFLOAD=y ++CONFIG_SND_JACK=y ++CONFIG_SND_JACK_INPUT_DEV=y ++# CONFIG_SND_OSSEMUL is not set ++CONFIG_SND_PCM_TIMER=y ++# CONFIG_SND_HRTIMER is not set ++CONFIG_SND_DYNAMIC_MINORS=y ++CONFIG_SND_MAX_CARDS=32 ++CONFIG_SND_SUPPORT_OLD_API=y ++CONFIG_SND_PROC_FS=y ++CONFIG_SND_VERBOSE_PROCFS=y ++# CONFIG_SND_VERBOSE_PRINTK is not set ++# CONFIG_SND_DEBUG is not set ++CONFIG_SND_VMASTER=y ++# CONFIG_SND_SEQUENCER is not set ++CONFIG_SND_DRIVERS=y ++# CONFIG_SND_DUMMY is not set + CONFIG_SND_ALOOP=m ++# CONFIG_SND_MTPAV is not set ++# CONFIG_SND_SERIAL_U16550 is not set ++# CONFIG_SND_MPU401 is not set ++CONFIG_SND_PCI=y ++# CONFIG_SND_AD1889 is not set ++# CONFIG_SND_ALS300 is not set ++# CONFIG_SND_ALI5451 is not set ++# CONFIG_SND_ATIIXP is not set ++# CONFIG_SND_ATIIXP_MODEM is not set ++# CONFIG_SND_AU8810 is not set ++# CONFIG_SND_AU8820 is not set ++# CONFIG_SND_AU8830 is not set ++# CONFIG_SND_AW2 is not set ++# CONFIG_SND_AZT3328 is not set ++# CONFIG_SND_BT87X is not set ++# CONFIG_SND_CA0106 is not set ++# CONFIG_SND_CMIPCI is not set ++# CONFIG_SND_OXYGEN is not set ++# CONFIG_SND_CS4281 is not set ++# CONFIG_SND_CS46XX is not set ++# CONFIG_SND_CTXFI is not set ++# CONFIG_SND_DARLA20 is not set ++# CONFIG_SND_GINA20 is not set ++# CONFIG_SND_LAYLA20 is not set ++# CONFIG_SND_DARLA24 is not set ++# CONFIG_SND_GINA24 is not set ++# CONFIG_SND_LAYLA24 is not set ++# CONFIG_SND_MONA is not set ++# CONFIG_SND_MIA is not set ++# CONFIG_SND_ECHO3G is not set ++# CONFIG_SND_INDIGO is not set ++# CONFIG_SND_INDIGOIO is not set ++# CONFIG_SND_INDIGODJ is not set ++# CONFIG_SND_INDIGOIOX is not set ++# CONFIG_SND_INDIGODJX is not set ++# CONFIG_SND_EMU10K1 is not set ++# CONFIG_SND_EMU10K1X is not set ++# CONFIG_SND_ENS1370 is not set ++# CONFIG_SND_ENS1371 is not set ++# CONFIG_SND_ES1938 is not set ++# CONFIG_SND_ES1968 is not set ++# CONFIG_SND_FM801 is not set ++# CONFIG_SND_HDSP is not set ++# CONFIG_SND_HDSPM is not set ++# CONFIG_SND_ICE1712 is not set ++# CONFIG_SND_ICE1724 is not set ++# CONFIG_SND_INTEL8X0 is not set ++# CONFIG_SND_INTEL8X0M is not set ++# CONFIG_SND_KORG1212 is not set ++# CONFIG_SND_LOLA is not set ++# CONFIG_SND_LX6464ES is not set ++# CONFIG_SND_MAESTRO3 is not set ++# CONFIG_SND_MIXART is not set ++# CONFIG_SND_NM256 is not set ++# CONFIG_SND_PCXHR is not set ++# CONFIG_SND_RIPTIDE is not set ++# CONFIG_SND_RME32 is not set ++# CONFIG_SND_RME96 is not set ++# CONFIG_SND_RME9652 is not set ++# CONFIG_SND_SE6X is not set ++# CONFIG_SND_SONICVIBES is not set ++# CONFIG_SND_TRIDENT is not set ++# CONFIG_SND_VIA82XX is not set ++# CONFIG_SND_VIA82XX_MODEM is not set ++# CONFIG_SND_VIRTUOSO is not set ++# CONFIG_SND_VX222 is not set ++# CONFIG_SND_YMFPCI is not set ++ ++# ++# HD-Audio ++# ++CONFIG_SND_HDA=m ++# CONFIG_SND_HDA_INTEL is not set + CONFIG_SND_HDA_TEGRA=m ++# CONFIG_SND_HDA_HWDEP is not set ++# CONFIG_SND_HDA_RECONFIG is not set ++# CONFIG_SND_HDA_INPUT_BEEP is not set ++# CONFIG_SND_HDA_PATCH_LOADER is not set ++# CONFIG_SND_HDA_CODEC_REALTEK is not set ++# CONFIG_SND_HDA_CODEC_ANALOG is not set ++# CONFIG_SND_HDA_CODEC_SIGMATEL is not set ++# CONFIG_SND_HDA_CODEC_VIA is not set + CONFIG_SND_HDA_CODEC_HDMI=m ++# CONFIG_SND_HDA_CODEC_CIRRUS is not set ++# CONFIG_SND_HDA_CODEC_CONEXANT is not set ++# CONFIG_SND_HDA_CODEC_CA0110 is not set ++# CONFIG_SND_HDA_CODEC_CA0132 is not set ++# CONFIG_SND_HDA_CODEC_CMEDIA is not set ++# CONFIG_SND_HDA_CODEC_SI3054 is not set ++# CONFIG_SND_HDA_GENERIC is not set + CONFIG_SND_HDA_POWER_SAVE_DEFAULT=1 ++# end of HD-Audio ++ ++CONFIG_SND_HDA_CORE=m ++CONFIG_SND_HDA_ALIGNED_MMIO=y ++CONFIG_SND_HDA_PREALLOC_SIZE=64 ++CONFIG_SND_SPI=y ++CONFIG_SND_USB=y + CONFIG_SND_USB_AUDIO=y ++CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER=y ++# CONFIG_SND_USB_UA101 is not set ++# CONFIG_SND_USB_CAIAQ is not set ++# CONFIG_SND_USB_6FIRE is not set ++# CONFIG_SND_USB_HIFACE is not set ++# CONFIG_SND_BCD2000 is not set ++# CONFIG_SND_USB_POD is not set ++# CONFIG_SND_USB_PODHD is not set ++# CONFIG_SND_USB_TONEPORT is not set ++# CONFIG_SND_USB_VARIAX is not set + CONFIG_SND_SOC=y ++CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y ++CONFIG_SND_SOC_COMPRESS=y ++# CONFIG_SND_SOC_AMD_ACP is not set ++# CONFIG_SND_ATMEL_SOC is not set ++# CONFIG_SND_BCM63XX_I2S_WHISTLER is not set ++# CONFIG_SND_DESIGNWARE_I2S is not set ++ ++# ++# SoC Audio for Freescale CPUs ++# ++ ++# ++# Common SoC Audio options for Freescale CPUs: ++# ++# CONFIG_SND_SOC_FSL_ASRC is not set ++# CONFIG_SND_SOC_FSL_SAI is not set ++# CONFIG_SND_SOC_FSL_AUDMIX is not set ++# CONFIG_SND_SOC_FSL_SSI is not set ++# CONFIG_SND_SOC_FSL_SPDIF is not set ++# CONFIG_SND_SOC_FSL_ESAI is not set ++# CONFIG_SND_SOC_FSL_MICFIL is not set ++# CONFIG_SND_SOC_IMX_AUDMUX is not set ++# end of SoC Audio for Freescale CPUs ++ ++# CONFIG_SND_I2S_HI6210_I2S is not set ++# CONFIG_SND_SOC_IMG is not set ++# CONFIG_SND_SOC_MTK_BTCVSD is not set ++# CONFIG_SND_SOC_SOF_TOPLEVEL is not set ++ ++# ++# STMicroelectronics STM32 SOC audio support ++# ++# end of STMicroelectronics STM32 SOC audio support ++ + CONFIG_SND_SOC_TEGRA=m ++# CONFIG_SND_SOC_TEGRA20_AC97 is not set ++# CONFIG_SND_SOC_TEGRA20_DAS is not set ++# CONFIG_SND_SOC_TEGRA20_I2S is not set ++CONFIG_SND_SOC_TEGRA20_SPDIF=m ++# CONFIG_SND_SOC_TEGRA30_AHUB is not set ++# CONFIG_SND_SOC_TEGRA30_I2S is not set + CONFIG_SND_SOC_TEGRA210_AHUB=m + CONFIG_SND_SOC_TEGRA210_DMIC=m + CONFIG_SND_SOC_TEGRA210_I2S=m +@@ -985,20 +5567,199 @@ CONFIG_SND_SOC_TEGRA210_OPE=m + CONFIG_SND_SOC_TEGRA210_ADSP=m + CONFIG_SND_SOC_TEGRA210_AUDIO=m + CONFIG_SND_SOC_TEGRA_AUDIO_GRAPH_CARD=m ++# CONFIG_SND_SOC_TEGRA_RT5640 is not set ++# CONFIG_SND_SOC_TEGRA_WM8753 is not set ++# CONFIG_SND_SOC_TEGRA_WM8903 is not set ++# CONFIG_SND_SOC_TEGRA_WM9712 is not set ++# CONFIG_SND_SOC_TEGRA_TRIMSLICE is not set ++# CONFIG_SND_SOC_TEGRA_ALC5632 is not set ++# CONFIG_SND_SOC_TEGRA_MAX98090 is not set ++# CONFIG_SND_SOC_TEGRA_RT5677 is not set ++# CONFIG_SND_SOC_TEGRA_SGTL5000 is not set ++# CONFIG_SND_SOC_XILINX_I2S is not set ++# CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER is not set ++# CONFIG_SND_SOC_XILINX_SPDIF is not set ++# CONFIG_SND_SOC_XTFPGA_I2S is not set ++# CONFIG_ZX_TDM is not set ++CONFIG_SND_SOC_I2C_AND_SPI=y ++ ++# ++# CODEC drivers ++# ++# CONFIG_SND_SOC_AC97_CODEC is not set ++# CONFIG_SND_SOC_ADAU1701 is not set ++# CONFIG_SND_SOC_ADAU1761_I2C is not set ++# CONFIG_SND_SOC_ADAU1761_SPI is not set ++# CONFIG_SND_SOC_ADAU7002 is not set ++# CONFIG_SND_SOC_ADAU7118_HW is not set ++# CONFIG_SND_SOC_ADAU7118_I2C is not set ++# CONFIG_SND_SOC_AK4104 is not set ++# CONFIG_SND_SOC_AK4118 is not set ++# CONFIG_SND_SOC_AK4458 is not set ++# CONFIG_SND_SOC_AK4554 is not set + CONFIG_SND_SOC_AK4613=m ++# CONFIG_SND_SOC_AK4642 is not set ++# CONFIG_SND_SOC_AK5386 is not set ++# CONFIG_SND_SOC_AK5558 is not set ++# CONFIG_SND_SOC_ALC5623 is not set ++# CONFIG_SND_SOC_BD28623 is not set ++# CONFIG_SND_SOC_BT_SCO is not set ++# CONFIG_SND_SOC_CS35L32 is not set ++# CONFIG_SND_SOC_CS35L33 is not set ++# CONFIG_SND_SOC_CS35L34 is not set ++# CONFIG_SND_SOC_CS35L35 is not set ++# CONFIG_SND_SOC_CS35L36 is not set ++# CONFIG_SND_SOC_CS42L42 is not set ++# CONFIG_SND_SOC_CS42L51_I2C is not set ++# CONFIG_SND_SOC_CS42L52 is not set ++# CONFIG_SND_SOC_CS42L56 is not set ++# CONFIG_SND_SOC_CS42L73 is not set ++# CONFIG_SND_SOC_CS4234 is not set ++# CONFIG_SND_SOC_CS4265 is not set ++# CONFIG_SND_SOC_CS4270 is not set ++# CONFIG_SND_SOC_CS4271_I2C is not set ++# CONFIG_SND_SOC_CS4271_SPI is not set ++# CONFIG_SND_SOC_CS42XX8_I2C is not set ++# CONFIG_SND_SOC_CS43130 is not set ++# CONFIG_SND_SOC_CS4341 is not set ++# CONFIG_SND_SOC_CS4349 is not set ++# CONFIG_SND_SOC_CS53L30 is not set ++# CONFIG_SND_SOC_CX2072X is not set ++# CONFIG_SND_SOC_DA7213 is not set ++# CONFIG_SND_SOC_DMIC is not set ++CONFIG_SND_SOC_HDMI_CODEC=m + CONFIG_SND_SOC_ES7134=m ++# CONFIG_SND_SOC_ES7241 is not set ++# CONFIG_SND_SOC_ES8316 is not set ++# CONFIG_SND_SOC_ES8328_I2C is not set ++# CONFIG_SND_SOC_ES8328_SPI is not set ++# CONFIG_SND_SOC_GTM601 is not set ++# CONFIG_SND_SOC_INNO_RK3036 is not set ++# CONFIG_SND_SOC_MAX98088 is not set ++# CONFIG_SND_SOC_MAX98357A is not set ++# CONFIG_SND_SOC_MAX98504 is not set ++# CONFIG_SND_SOC_MAX9867 is not set + CONFIG_SND_SOC_MAX98927=m ++# CONFIG_SND_SOC_MAX98373_I2C is not set ++# CONFIG_SND_SOC_MAX98373_SDW is not set ++# CONFIG_SND_SOC_MAX98390 is not set ++# CONFIG_SND_SOC_MAX9860 is not set ++# CONFIG_SND_SOC_MSM8916_WCD_ANALOG is not set ++# CONFIG_SND_SOC_MSM8916_WCD_DIGITAL is not set ++# CONFIG_SND_SOC_PCM1681 is not set ++# CONFIG_SND_SOC_PCM1789_I2C is not set ++# CONFIG_SND_SOC_PCM179X_I2C is not set ++# CONFIG_SND_SOC_PCM179X_SPI is not set ++# CONFIG_SND_SOC_PCM186X_I2C is not set ++# CONFIG_SND_SOC_PCM186X_SPI is not set ++# CONFIG_SND_SOC_PCM3060_I2C is not set ++# CONFIG_SND_SOC_PCM3060_SPI is not set ++CONFIG_SND_SOC_PCM3168A=m + CONFIG_SND_SOC_PCM3168A_I2C=m ++# CONFIG_SND_SOC_PCM3168A_SPI is not set ++# CONFIG_SND_SOC_PCM512x_I2C is not set ++# CONFIG_SND_SOC_PCM512x_SPI is not set ++# CONFIG_SND_SOC_RK3328 is not set ++CONFIG_SND_SOC_RL6231=m ++# CONFIG_SND_SOC_RT1308_SDW is not set ++# CONFIG_SND_SOC_RT5616 is not set ++# CONFIG_SND_SOC_RT5631 is not set + CONFIG_SND_SOC_RT5640=m ++CONFIG_SND_SOC_RT5659=m ++# CONFIG_SND_SOC_RT5682_SDW is not set ++# CONFIG_SND_SOC_RT700_SDW is not set ++# CONFIG_SND_SOC_RT711_SDW is not set ++# CONFIG_SND_SOC_RT715_SDW is not set ++CONFIG_SND_SOC_SGTL5000=m ++# CONFIG_SND_SOC_SIMPLE_AMPLIFIER is not set ++# CONFIG_SND_SOC_SIRF_AUDIO_CODEC is not set + CONFIG_SND_SOC_SPDIF=m ++# CONFIG_SND_SOC_SSM2305 is not set ++# CONFIG_SND_SOC_SSM2602_SPI is not set ++# CONFIG_SND_SOC_SSM2602_I2C is not set ++# CONFIG_SND_SOC_SSM4567 is not set ++# CONFIG_SND_SOC_STA32X is not set ++# CONFIG_SND_SOC_STA350 is not set ++# CONFIG_SND_SOC_STI_SAS is not set ++CONFIG_SND_SOC_TAS2552=m ++# CONFIG_SND_SOC_TAS2562 is not set ++# CONFIG_SND_SOC_TAS2764 is not set ++# CONFIG_SND_SOC_TAS2770 is not set ++# CONFIG_SND_SOC_TAS5086 is not set + CONFIG_SND_SOC_TAS571X=m ++# CONFIG_SND_SOC_TAS5720 is not set ++# CONFIG_SND_SOC_TAS6424 is not set ++# CONFIG_SND_SOC_TDA7419 is not set ++# CONFIG_SND_SOC_TFA9879 is not set ++# CONFIG_SND_SOC_TLV320AIC23_I2C is not set ++# CONFIG_SND_SOC_TLV320AIC23_SPI is not set ++# CONFIG_SND_SOC_TLV320AIC31XX is not set ++# CONFIG_SND_SOC_TLV320AIC32X4_I2C is not set ++# CONFIG_SND_SOC_TLV320AIC32X4_SPI is not set ++# CONFIG_SND_SOC_TLV320AIC3X is not set ++# CONFIG_SND_SOC_TLV320ADCX140 is not set ++# CONFIG_SND_SOC_TS3A227E is not set ++# CONFIG_SND_SOC_TSCS42XX is not set ++# CONFIG_SND_SOC_TSCS454 is not set ++# CONFIG_SND_SOC_UDA1334 is not set ++# CONFIG_SND_SOC_WM8510 is not set ++# CONFIG_SND_SOC_WM8523 is not set ++# CONFIG_SND_SOC_WM8524 is not set ++# CONFIG_SND_SOC_WM8580 is not set ++# CONFIG_SND_SOC_WM8711 is not set ++# CONFIG_SND_SOC_WM8728 is not set ++# CONFIG_SND_SOC_WM8731 is not set ++# CONFIG_SND_SOC_WM8737 is not set ++# CONFIG_SND_SOC_WM8741 is not set ++# CONFIG_SND_SOC_WM8750 is not set ++# CONFIG_SND_SOC_WM8753 is not set ++# CONFIG_SND_SOC_WM8770 is not set ++# CONFIG_SND_SOC_WM8776 is not set ++# CONFIG_SND_SOC_WM8782 is not set ++# CONFIG_SND_SOC_WM8804_I2C is not set ++# CONFIG_SND_SOC_WM8804_SPI is not set ++# CONFIG_SND_SOC_WM8903 is not set ++# CONFIG_SND_SOC_WM8904 is not set ++# CONFIG_SND_SOC_WM8960 is not set ++# CONFIG_SND_SOC_WM8962 is not set ++# CONFIG_SND_SOC_WM8974 is not set ++# CONFIG_SND_SOC_WM8978 is not set ++# CONFIG_SND_SOC_WM8985 is not set ++# CONFIG_SND_SOC_WSA881X is not set ++# CONFIG_SND_SOC_ZL38060 is not set ++# CONFIG_SND_SOC_ZX_AUD96P22 is not set ++# CONFIG_SND_SOC_MAX9759 is not set ++# CONFIG_SND_SOC_MT6351 is not set ++# CONFIG_SND_SOC_MT6358 is not set ++# CONFIG_SND_SOC_MT6660 is not set ++# CONFIG_SND_SOC_NAU8540 is not set ++# CONFIG_SND_SOC_NAU8810 is not set ++# CONFIG_SND_SOC_NAU8822 is not set ++# CONFIG_SND_SOC_NAU8824 is not set ++# CONFIG_SND_SOC_TPA6130A2 is not set ++# end of CODEC drivers ++ ++CONFIG_SND_SIMPLE_CARD_UTILS=m + CONFIG_SND_SIMPLE_CARD=m + CONFIG_SND_AUDIO_GRAPH_CARD=m + CONFIG_SND_SOC_TEGRA210_ADSP_VIRT_ALT=m + CONFIG_SND_SOC_TEGRA_VIRT_T210REF_PCM=m ++CONFIG_SND_T23X_SAFETY_I2S=m ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set + CONFIG_HIDRAW=y + CONFIG_UHID=y ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# + CONFIG_HID_A4TECH=m ++# CONFIG_HID_ACCUTOUCH is not set + CONFIG_HID_ACRUX=y + CONFIG_HID_ACRUX_FF=y + CONFIG_HID_APPLE=y +@@ -1007,41 +5768,62 @@ CONFIG_HID_ASUS=m + CONFIG_HID_AUREAL=m + CONFIG_HID_BELKIN=m + CONFIG_HID_BETOP_FF=m ++# CONFIG_HID_BIGBEN_FF is not set + CONFIG_HID_CHERRY=m + CONFIG_HID_CHICONY=m + CONFIG_HID_CORSAIR=m ++# CONFIG_HID_COUGAR is not set ++# CONFIG_HID_MACALLY is not set + CONFIG_HID_PRODIKEYS=m + CONFIG_HID_CMEDIA=m ++# CONFIG_HID_CP2112 is not set ++# CONFIG_HID_CREATIVE_SB0540 is not set + CONFIG_HID_CYPRESS=m + CONFIG_HID_DRAGONRISE=y + CONFIG_DRAGONRISE_FF=y + CONFIG_HID_EMS_FF=m ++# CONFIG_HID_ELAN is not set + CONFIG_HID_ELECOM=m + CONFIG_HID_ELO=m + CONFIG_HID_EZKEY=m + CONFIG_HID_GEMBIRD=m + CONFIG_HID_GFRM=m ++# CONFIG_HID_GLORIOUS is not set + CONFIG_HID_HOLTEK=y ++# CONFIG_HOLTEK_FF is not set ++# CONFIG_HID_VIVALDI is not set + CONFIG_HID_GT683R=m + CONFIG_HID_KEYTOUCH=y + CONFIG_HID_KYE=y + CONFIG_HID_UCLOGIC=y + CONFIG_HID_WALTOP=y ++# CONFIG_HID_VIEWSONIC is not set + CONFIG_HID_GYRATION=y + CONFIG_HID_ICADE=m + CONFIG_HID_ITE=m ++# CONFIG_HID_JABRA is not set + CONFIG_HID_TWINHAN=y + CONFIG_HID_KENSINGTON=m + CONFIG_HID_LCPOWER=y ++CONFIG_HID_LED=m + CONFIG_HID_LENOVO=m + CONFIG_HID_LOGITECH=m + CONFIG_HID_LOGITECH_DJ=m ++CONFIG_HID_LOGITECH_HIDPP=m ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set + CONFIG_HID_MAGICMOUSE=y ++# CONFIG_HID_MALTRON is not set ++# CONFIG_HID_MAYFLASH is not set ++# CONFIG_HID_REDRAGON is not set + CONFIG_HID_MICROSOFT=m + CONFIG_HID_MONTEREY=m + CONFIG_HID_MULTITOUCH=y + CONFIG_HID_NTI=m + CONFIG_HID_NTRIG=y ++# CONFIG_HID_NVIDIA_STAND is not set + CONFIG_HID_ORTEK=y + CONFIG_HID_PANTHERLORD=y + CONFIG_PANTHERLORD_FF=y +@@ -1050,6 +5832,9 @@ CONFIG_HID_PETALYNX=m + CONFIG_HID_PICOLCD=m + CONFIG_HID_PICOLCD_FB=y + CONFIG_HID_PICOLCD_BACKLIGHT=y ++# CONFIG_HID_PICOLCD_LCD is not set ++# CONFIG_HID_PICOLCD_LEDS is not set ++# CONFIG_HID_PICOLCD_CIR is not set + CONFIG_HID_PLANTRONICS=m + CONFIG_HID_PRIMAX=m + CONFIG_HID_RETRODE=m +@@ -1057,7 +5842,9 @@ CONFIG_HID_ROCCAT=m + CONFIG_HID_SAITEK=m + CONFIG_HID_SAMSUNG=m + CONFIG_HID_SONY=m ++# CONFIG_SONY_FF is not set + CONFIG_HID_SPEEDLINK=m ++# CONFIG_HID_STEAM is not set + CONFIG_HID_STEELSERIES=m + CONFIG_HID_SUNPLUS=m + CONFIG_HID_RMI=m +@@ -1069,28 +5856,112 @@ CONFIG_HID_TIVO=m + CONFIG_HID_TOPSEED=m + CONFIG_HID_THINGM=m + CONFIG_HID_THRUSTMASTER=m ++# CONFIG_THRUSTMASTER_FF is not set + CONFIG_HID_UDRAW_PS3=m ++# CONFIG_HID_U2FZERO is not set + CONFIG_HID_WACOM=m + CONFIG_HID_WIIMOTE=m + CONFIG_HID_XINMO=m + CONFIG_HID_ZEROPLUS=m ++# CONFIG_ZEROPLUS_FF is not set + CONFIG_HID_ZYDACRON=m + CONFIG_HID_SENSOR_HUB=m + CONFIG_HID_SENSOR_CUSTOM_SENSOR=m + CONFIG_HID_ALPS=m ++# CONFIG_HID_MCP2221 is not set ++# end of Special HID drivers ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set + CONFIG_USB_HIDDEV=y ++# end of USB HID support ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++# end of I2C HID support ++ ++# ++# SHIELD accessory HID drivers ++# ++# CONFIG_HID_SHIELD_BLAKE is not set + CONFIG_HID_SHIELD_REMOTE=m ++# end of SHIELD accessory HID drivers ++# end of HID support ++ ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++# CONFIG_USB_LED_TRIG is not set ++CONFIG_USB_ULPI_BUS=m ++CONFIG_USB_CONN_GPIO=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++CONFIG_USB_PCI=y + CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_FEW_INIT_RETRIES is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set + CONFIG_USB_OTG=y ++# CONFIG_USB_OTG_PRODUCTLIST is not set ++# CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_LEDS_TRIGGER_USBPORT is not set ++CONFIG_USB_AUTOSUSPEND_DELAY=2 + CONFIG_USB_MON=m ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set + CONFIG_USB_XHCI_HCD=y ++# CONFIG_USB_XHCI_DBGCAP is not set ++CONFIG_USB_XHCI_PCI=y ++# CONFIG_USB_XHCI_PCI_RENESAS is not set ++# CONFIG_USB_XHCI_PLATFORM is not set + CONFIG_USB_XHCI_TEGRA=y ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set + CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PCI=y + CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_UHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_BCMA is not set ++# CONFIG_USB_HCD_SSB is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# + CONFIG_USB_ACM=m + CONFIG_USB_PRINTER=m ++CONFIG_USB_WDM=m ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# + CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set + CONFIG_USB_STORAGE_REALTEK=m ++CONFIG_REALTEK_AUTOPM=y + CONFIG_USB_STORAGE_DATAFAB=m + CONFIG_USB_STORAGE_FREECOM=m + CONFIG_USB_STORAGE_ISD200=m +@@ -1104,100 +5975,723 @@ CONFIG_USB_STORAGE_KARMA=m + CONFIG_USB_STORAGE_CYPRESS_ATACB=m + CONFIG_USB_STORAGE_ENE_UB6250=m + CONFIG_USB_UAS=y ++ ++# ++# USB Imaging devices ++# + CONFIG_USB_MDC800=m ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_CDNS3 is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set + CONFIG_USB_CHIPIDEA=m ++# CONFIG_USB_CHIPIDEA_UDC is not set ++CONFIG_USB_CHIPIDEA_MSM=m ++CONFIG_USB_CHIPIDEA_IMX=m ++CONFIG_USB_CHIPIDEA_GENERIC=m ++# CONFIG_USB_ISP1760 is not set ++ ++# ++# USB port drivers ++# + CONFIG_USB_SERIAL=m ++# CONFIG_USB_SERIAL_GENERIC is not set ++# CONFIG_USB_SERIAL_SIMPLE is not set ++# CONFIG_USB_SERIAL_AIRCABLE is not set ++# CONFIG_USB_SERIAL_ARK3116 is not set ++# CONFIG_USB_SERIAL_BELKIN is not set + CONFIG_USB_SERIAL_CH341=m ++# CONFIG_USB_SERIAL_WHITEHEAT is not set ++# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set + CONFIG_USB_SERIAL_CP210X=m ++# CONFIG_USB_SERIAL_CYPRESS_M8 is not set ++# CONFIG_USB_SERIAL_EMPEG is not set + CONFIG_USB_SERIAL_FTDI_SIO=m ++# CONFIG_USB_SERIAL_VISOR is not set ++# CONFIG_USB_SERIAL_IPAQ is not set ++# CONFIG_USB_SERIAL_IR is not set ++# CONFIG_USB_SERIAL_EDGEPORT is not set ++# CONFIG_USB_SERIAL_EDGEPORT_TI is not set ++# CONFIG_USB_SERIAL_F81232 is not set ++# CONFIG_USB_SERIAL_F8153X is not set + CONFIG_USB_SERIAL_GARMIN=m ++# CONFIG_USB_SERIAL_IPW is not set ++# CONFIG_USB_SERIAL_IUU is not set ++# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set + CONFIG_USB_SERIAL_KEYSPAN=m ++# CONFIG_USB_SERIAL_KLSI is not set ++# CONFIG_USB_SERIAL_KOBIL_SCT is not set ++# CONFIG_USB_SERIAL_MCT_U232 is not set ++# CONFIG_USB_SERIAL_METRO is not set ++# CONFIG_USB_SERIAL_MOS7720 is not set ++# CONFIG_USB_SERIAL_MOS7840 is not set ++# CONFIG_USB_SERIAL_MXUPORT is not set ++# CONFIG_USB_SERIAL_NAVMAN is not set + CONFIG_USB_SERIAL_PL2303=m ++# CONFIG_USB_SERIAL_OTI6858 is not set ++# CONFIG_USB_SERIAL_QCAUX is not set ++# CONFIG_USB_SERIAL_QUALCOMM is not set ++# CONFIG_USB_SERIAL_SPCP8X5 is not set ++# CONFIG_USB_SERIAL_SAFE is not set ++# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set ++# CONFIG_USB_SERIAL_SYMBOL is not set ++# CONFIG_USB_SERIAL_TI is not set ++# CONFIG_USB_SERIAL_CYBERJACK is not set ++# CONFIG_USB_SERIAL_XIRCOM is not set ++CONFIG_USB_SERIAL_WWAN=m + CONFIG_USB_SERIAL_OPTION=m ++# CONFIG_USB_SERIAL_OMNINET is not set ++# CONFIG_USB_SERIAL_OPTICON is not set + CONFIG_USB_SERIAL_XSENS_MT=m ++# CONFIG_USB_SERIAL_WISHBONE is not set ++# CONFIG_USB_SERIAL_SSU100 is not set ++# CONFIG_USB_SERIAL_QT2 is not set ++# CONFIG_USB_SERIAL_UPD78F0730 is not set ++# CONFIG_USB_SERIAL_DEBUG is not set ++ ++# ++# USB Miscellaneous drivers ++# + CONFIG_USB_EMI62=m + CONFIG_USB_EMI26=m ++# CONFIG_USB_ADUTUX is not set + CONFIG_USB_SEVSEG=m ++# CONFIG_USB_LEGOTOWER is not set + CONFIG_USB_LCD=m + CONFIG_USB_CYPRESS_CY7C63=m + CONFIG_USB_CYTHERM=m + CONFIG_USB_IDMOUSE=m ++# CONFIG_USB_FTDI_ELAN is not set + CONFIG_USB_APPLEDISPLAY=m ++# CONFIG_APPLE_MFI_FASTCHARGE is not set + CONFIG_USB_LD=m ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set + CONFIG_USB_TEST=m + CONFIG_USB_EHSET_TEST_FIXTURE=m ++# CONFIG_USB_ISIGHTFW is not set + CONFIG_USB_YUREX=m ++CONFIG_USB_EZUSB_FX2=m ++# CONFIG_USB_HUB_USB251XB is not set ++# CONFIG_USB_HSIC_USB3503 is not set + CONFIG_USB_HSIC_USB4604=m ++# CONFIG_USB_LINK_LAYER_TEST is not set ++# CONFIG_USB_CHAOSKEY is not set ++ ++# ++# USB Physical Layer drivers ++# ++CONFIG_USB_PHY=y ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ISP1301 is not set ++CONFIG_USB_TEGRA_PHY=m ++CONFIG_USB_ULPI=y ++CONFIG_USB_ULPI_VIEWPORT=y ++# end of USB Physical Layer drivers ++ + CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++# CONFIG_USB_GADGET_DEBUG_FS is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++# CONFIG_U_SERIAL_CONSOLE is not set ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_SNP_UDC_PLAT is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_BDC_UDC is not set ++# CONFIG_USB_AMD5536UDC is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_NET2280 is not set ++# CONFIG_USB_GOKU is not set ++# CONFIG_USB_EG20T is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_MAX3420_UDC is not set + CONFIG_USB_TEGRA_XUDC=y ++# CONFIG_USB_DUMMY_HCD is not set ++# end of USB Peripheral Controller ++ ++CONFIG_USB_LIBCOMPOSITE=y ++CONFIG_USB_F_ACM=y ++CONFIG_USB_F_SS_LB=y ++CONFIG_USB_U_SERIAL=y ++CONFIG_USB_U_ETHER=y ++CONFIG_USB_F_NCM=y ++CONFIG_USB_F_ECM=y ++CONFIG_USB_F_RNDIS=y ++CONFIG_USB_F_MASS_STORAGE=y ++CONFIG_USB_F_FS=y ++CONFIG_USB_F_ACC=y + CONFIG_USB_CONFIGFS=y ++# CONFIG_USB_CONFIGFS_SERIAL is not set + CONFIG_USB_CONFIGFS_ACM=y ++# CONFIG_USB_CONFIGFS_OBEX is not set + CONFIG_USB_CONFIGFS_NCM=y + CONFIG_USB_CONFIGFS_ECM=y ++# CONFIG_USB_CONFIGFS_ECM_SUBSET is not set + CONFIG_USB_CONFIGFS_RNDIS=y ++# CONFIG_USB_CONFIGFS_EEM is not set + CONFIG_USB_CONFIGFS_MASS_STORAGE=y + CONFIG_USB_CONFIGFS_F_LB_SS=y + CONFIG_USB_CONFIGFS_F_FS=y + CONFIG_USB_CONFIGFS_F_ACC=y ++# CONFIG_USB_CONFIGFS_F_UAC1 is not set ++# CONFIG_USB_CONFIGFS_F_UAC1_LEGACY is not set ++# CONFIG_USB_CONFIGFS_F_UAC2 is not set ++# CONFIG_USB_CONFIGFS_F_MIDI is not set ++# CONFIG_USB_CONFIGFS_F_HID is not set ++# CONFIG_USB_CONFIGFS_F_UVC is not set ++# CONFIG_USB_CONFIGFS_F_PRINTER is not set ++ ++# ++# USB Gadget precomposed configurations ++# ++# CONFIG_USB_ZERO is not set ++# CONFIG_USB_AUDIO is not set ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++# CONFIG_USB_MASS_STORAGE is not set ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_MIDI_GADGET is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++# CONFIG_USB_RAW_GADGET is not set ++# end of USB Gadget precomposed configurations ++ + CONFIG_TYPEC=m +-CONFIG_TYPEC_FUSB301=m ++# CONFIG_TYPEC_TCPM is not set + CONFIG_TYPEC_UCSI=m + CONFIG_UCSI_CCG=m ++# CONFIG_UCSI_ACPI is not set ++# CONFIG_TYPEC_HD3SS3220 is not set ++# CONFIG_TYPEC_TPS6598X is not set + CONFIG_TYPEC_STUSB160X=m ++CONFIG_TYPEC_FUSB301=m ++ ++# ++# USB Type-C Multiplexer/DeMultiplexer Switch support ++# ++# CONFIG_TYPEC_MUX_PI3USB30532 is not set ++# end of USB Type-C Multiplexer/DeMultiplexer Switch support ++ ++# ++# USB Type-C Alternate Mode drivers ++# ++# CONFIG_TYPEC_DP_ALTMODE is not set ++# end of USB Type-C Alternate Mode drivers ++ ++CONFIG_USB_ROLE_SWITCH=y + CONFIG_MMC=y ++CONFIG_PWRSEQ_EMMC=y ++# CONFIG_PWRSEQ_SD8787 is not set ++CONFIG_PWRSEQ_SIMPLE=y ++CONFIG_MMC_BLOCK=y + CONFIG_MMC_BLOCK_MINORS=32 ++# CONFIG_SDIO_UART is not set + CONFIG_MMC_TEST=m ++# CONFIG_MMC_FFU is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_DEBUG is not set + CONFIG_MMC_ARMMMCI=y + # CONFIG_MMC_STM32_SDMMC is not set + CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_IO_ACCESSORS=y ++# CONFIG_MMC_SDHCI_PCI is not set ++# CONFIG_MMC_SDHCI_ACPI is not set + CONFIG_MMC_SDHCI_PLTFM=y ++# CONFIG_MMC_SDHCI_OF_ARASAN is not set ++# CONFIG_MMC_SDHCI_OF_ASPEED is not set ++# CONFIG_MMC_SDHCI_OF_AT91 is not set ++# CONFIG_MMC_SDHCI_OF_DWCMSHC is not set ++# CONFIG_MMC_SDHCI_CADENCE is not set + CONFIG_MMC_SDHCI_TEGRA=y ++# CONFIG_MMC_SDHCI_F_SDH30 is not set ++# CONFIG_MMC_SDHCI_MILBEAUT is not set ++# CONFIG_MMC_TIFM_SD is not set + CONFIG_MMC_SPI=m ++# CONFIG_MMC_CB710 is not set ++# CONFIG_MMC_VIA_SDMMC is not set + CONFIG_MMC_DW=y ++CONFIG_MMC_DW_PLTFM=y ++# CONFIG_MMC_DW_BLUEFIELD is not set ++# CONFIG_MMC_DW_EXYNOS is not set ++# CONFIG_MMC_DW_HI3798CV200 is not set ++# CONFIG_MMC_DW_K3 is not set ++# CONFIG_MMC_DW_PCI is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_MMC_CQHCI=y ++# CONFIG_MMC_HSQ is not set ++# CONFIG_MMC_TOSHIBA_PCI is not set ++# CONFIG_MMC_MTK is not set + CONFIG_MMC_SDHCI_XENON=y ++# CONFIG_MMC_SDHCI_OMAP is not set ++# CONFIG_MMC_SDHCI_AM654 is not set ++# CONFIG_MEMSTICK is not set ++CONFIG_NEW_LEDS=y + CONFIG_LEDS_CLASS=y ++# CONFIG_LEDS_CLASS_FLASH is not set ++# CONFIG_LEDS_CLASS_MULTICOLOR is not set ++# CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set ++ ++# ++# LED drivers ++# ++# CONFIG_LEDS_AN30259A is not set ++# CONFIG_LEDS_AW2013 is not set ++# CONFIG_LEDS_BCM6328 is not set ++# CONFIG_LEDS_BCM6358 is not set ++# CONFIG_LEDS_CR0014114 is not set ++# CONFIG_LEDS_EL15203000 is not set ++# CONFIG_LEDS_LM3530 is not set ++# CONFIG_LEDS_LM3532 is not set ++# CONFIG_LEDS_LM3642 is not set ++# CONFIG_LEDS_LM3692X is not set ++# CONFIG_LEDS_PCA9532 is not set + CONFIG_LEDS_GPIO=m ++# CONFIG_LEDS_LP3944 is not set ++# CONFIG_LEDS_LP3952 is not set ++# CONFIG_LEDS_LP50XX is not set ++# CONFIG_LEDS_LP55XX_COMMON is not set ++# CONFIG_LEDS_LP8860 is not set ++# CONFIG_LEDS_PCA955X is not set ++# CONFIG_LEDS_PCA963X is not set ++# CONFIG_LEDS_DAC124S085 is not set + CONFIG_LEDS_PWM=m ++# CONFIG_LEDS_REGULATOR is not set + CONFIG_LEDS_BD2802=m ++# CONFIG_LEDS_LT3593 is not set ++# CONFIG_LEDS_TCA6507 is not set ++# CONFIG_LEDS_TLC591XX is not set ++# CONFIG_LEDS_LM355x is not set + CONFIG_LEDS_IS31FL319X=m ++# CONFIG_LEDS_IS31FL32XX is not set ++ ++# ++# LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM) ++# ++# CONFIG_LEDS_BLINKM is not set ++# CONFIG_LEDS_SYSCON is not set ++# CONFIG_LEDS_MLXREG is not set ++# CONFIG_LEDS_USER is not set ++# CONFIG_LEDS_SPI_BYTE is not set ++# CONFIG_LEDS_TI_LMU_COMMON is not set ++ ++# ++# LED Triggers ++# ++CONFIG_LEDS_TRIGGERS=y ++# CONFIG_LEDS_TRIGGER_TIMER is not set ++# CONFIG_LEDS_TRIGGER_ONESHOT is not set ++# CONFIG_LEDS_TRIGGER_DISK is not set ++# CONFIG_LEDS_TRIGGER_MTD is not set ++# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set ++# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set ++# CONFIG_LEDS_TRIGGER_CPU is not set ++# CONFIG_LEDS_TRIGGER_ACTIVITY is not set ++# CONFIG_LEDS_TRIGGER_GPIO is not set ++# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set ++ ++# ++# iptables trigger is under Netfilter config (LED target) ++# ++# CONFIG_LEDS_TRIGGER_TRANSIENT is not set ++# CONFIG_LEDS_TRIGGER_CAMERA is not set ++# CONFIG_LEDS_TRIGGER_PANIC is not set ++# CONFIG_LEDS_TRIGGER_NETDEV is not set ++# CONFIG_LEDS_TRIGGER_PATTERN is not set ++# CONFIG_LEDS_TRIGGER_AUDIO is not set ++# CONFIG_LEDS_CY8C is not set ++# CONFIG_ACCESSIBILITY is not set + CONFIG_INFINIBAND=m + CONFIG_INFINIBAND_USER_MAD=m + CONFIG_INFINIBAND_USER_ACCESS=m ++CONFIG_INFINIBAND_USER_MEM=y ++CONFIG_INFINIBAND_ON_DEMAND_PAGING=y ++CONFIG_INFINIBAND_ADDR_TRANS=y ++CONFIG_INFINIBAND_ADDR_TRANS_CONFIGFS=y ++CONFIG_INFINIBAND_VIRT_DMA=y + CONFIG_INFINIBAND_MTHCA=m ++CONFIG_INFINIBAND_MTHCA_DEBUG=y ++# CONFIG_INFINIBAND_CXGB4 is not set ++# CONFIG_INFINIBAND_EFA is not set ++# CONFIG_INFINIBAND_I40IW is not set + CONFIG_MLX4_INFINIBAND=m + CONFIG_MLX5_INFINIBAND=m ++# CONFIG_INFINIBAND_OCRDMA is not set ++# CONFIG_INFINIBAND_HNS is not set ++# CONFIG_RDMA_RXE is not set ++# CONFIG_RDMA_SIW is not set + CONFIG_INFINIBAND_IPOIB=m + CONFIG_INFINIBAND_IPOIB_CM=y ++CONFIG_INFINIBAND_IPOIB_DEBUG=y ++# CONFIG_INFINIBAND_IPOIB_DEBUG_DATA is not set + CONFIG_INFINIBAND_SRP=m ++# CONFIG_INFINIBAND_ISER is not set ++# CONFIG_INFINIBAND_RTRS_CLIENT is not set ++# CONFIG_INFINIBAND_RTRS_SERVER is not set ++CONFIG_EDAC_SUPPORT=y ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y + CONFIG_RTC_CLASS=y ++CONFIG_RTC_HCTOSYS=y + CONFIG_RTC_HCTOSYS_DEVICE="rtc1" ++CONFIG_RTC_SYSTOHC=y ++CONFIG_RTC_SYSTOHC_DEVICE="rtc1" ++# CONFIG_RTC_DEBUG is not set + # CONFIG_RTC_NVMEM is not set ++ ++# ++# RTC interfaces ++# ++CONFIG_RTC_INTF_SYSFS=y ++CONFIG_RTC_INTF_PROC=y ++CONFIG_RTC_INTF_DEV=y ++# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set ++# CONFIG_RTC_DRV_TEST is not set ++ ++# ++# I2C RTC drivers ++# ++# CONFIG_RTC_DRV_ABB5ZES3 is not set ++# CONFIG_RTC_DRV_ABEOZ9 is not set ++# CONFIG_RTC_DRV_ABX80X is not set ++# CONFIG_RTC_DRV_DS1307 is not set ++# CONFIG_RTC_DRV_DS1374 is not set ++# CONFIG_RTC_DRV_DS1672 is not set ++# CONFIG_RTC_DRV_HYM8563 is not set ++# CONFIG_RTC_DRV_MAX6900 is not set + CONFIG_RTC_DRV_MAX77686=y ++# CONFIG_RTC_DRV_RK808 is not set ++# CONFIG_RTC_DRV_RS5C372 is not set ++# CONFIG_RTC_DRV_ISL1208 is not set ++# CONFIG_RTC_DRV_ISL12022 is not set ++# CONFIG_RTC_DRV_ISL12026 is not set ++# CONFIG_RTC_DRV_X1205 is not set ++# CONFIG_RTC_DRV_PCF8523 is not set ++# CONFIG_RTC_DRV_PCF85063 is not set ++# CONFIG_RTC_DRV_PCF85363 is not set ++# CONFIG_RTC_DRV_PCF8563 is not set ++# CONFIG_RTC_DRV_PCF8583 is not set ++# CONFIG_RTC_DRV_M41T80 is not set ++# CONFIG_RTC_DRV_BQ32K is not set ++# CONFIG_RTC_DRV_S35390A is not set ++# CONFIG_RTC_DRV_FM3130 is not set ++# CONFIG_RTC_DRV_RX8010 is not set ++# CONFIG_RTC_DRV_RX8581 is not set + CONFIG_RTC_DRV_RX8025=m ++# CONFIG_RTC_DRV_EM3027 is not set ++# CONFIG_RTC_DRV_RV3028 is not set ++# CONFIG_RTC_DRV_RV3032 is not set ++# CONFIG_RTC_DRV_RV8803 is not set ++# CONFIG_RTC_DRV_S5M is not set ++# CONFIG_RTC_DRV_SD3078 is not set ++ ++# ++# SPI RTC drivers ++# ++# CONFIG_RTC_DRV_M41T93 is not set ++# CONFIG_RTC_DRV_M41T94 is not set ++# CONFIG_RTC_DRV_DS1302 is not set ++# CONFIG_RTC_DRV_DS1305 is not set ++# CONFIG_RTC_DRV_DS1343 is not set ++# CONFIG_RTC_DRV_DS1347 is not set ++# CONFIG_RTC_DRV_DS1390 is not set ++# CONFIG_RTC_DRV_MAX6916 is not set ++# CONFIG_RTC_DRV_R9701 is not set ++# CONFIG_RTC_DRV_RX4581 is not set ++# CONFIG_RTC_DRV_RX6110 is not set ++# CONFIG_RTC_DRV_RS5C348 is not set ++# CONFIG_RTC_DRV_MAX6902 is not set ++# CONFIG_RTC_DRV_PCF2123 is not set ++# CONFIG_RTC_DRV_MCP795 is not set ++CONFIG_RTC_I2C_AND_SPI=y ++ ++# ++# SPI and I2C RTC drivers ++# ++# CONFIG_RTC_DRV_DS3232 is not set ++# CONFIG_RTC_DRV_PCF2127 is not set ++# CONFIG_RTC_DRV_RV3029C2 is not set ++ ++# ++# Platform RTC drivers ++# ++# CONFIG_RTC_DRV_DS1286 is not set ++# CONFIG_RTC_DRV_DS1511 is not set ++# CONFIG_RTC_DRV_DS1553 is not set ++# CONFIG_RTC_DRV_DS1685_FAMILY is not set ++# CONFIG_RTC_DRV_DS1742 is not set ++# CONFIG_RTC_DRV_DS2404 is not set ++# CONFIG_RTC_DRV_EFI is not set ++# CONFIG_RTC_DRV_STK17TA8 is not set ++# CONFIG_RTC_DRV_M48T86 is not set ++# CONFIG_RTC_DRV_M48T35 is not set ++# CONFIG_RTC_DRV_M48T59 is not set ++# CONFIG_RTC_DRV_MSM6242 is not set ++# CONFIG_RTC_DRV_BQ4802 is not set ++# CONFIG_RTC_DRV_RP5C01 is not set ++# CONFIG_RTC_DRV_V3020 is not set ++# CONFIG_RTC_DRV_ZYNQMP is not set ++ ++# ++# on-CPU RTC drivers ++# ++# CONFIG_RTC_DRV_PL030 is not set ++# CONFIG_RTC_DRV_PL031 is not set ++# CONFIG_RTC_DRV_CADENCE is not set ++# CONFIG_RTC_DRV_FTRTC010 is not set ++CONFIG_RTC_DRV_TEGRA=y ++# CONFIG_RTC_DRV_R7301 is not set ++ ++# ++# HID Sensor RTC drivers ++# ++# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set ++CONFIG_NVVRS_PSEQ_RTC=y + CONFIG_DMADEVICES=y ++# CONFIG_DMADEVICES_DEBUG is not set ++ ++# ++# DMA Devices ++# ++CONFIG_DMA_ENGINE=y ++CONFIG_DMA_VIRTUAL_CHANNELS=y ++CONFIG_DMA_ACPI=y ++CONFIG_DMA_OF=y ++# CONFIG_ALTERA_MSGDMA is not set ++# CONFIG_AMBA_PL08X is not set ++# CONFIG_BCM_SBA_RAID is not set ++# CONFIG_DW_AXI_DMAC is not set ++# CONFIG_FSL_EDMA is not set ++# CONFIG_FSL_QDMA is not set ++# CONFIG_HISI_DMA is not set ++# CONFIG_INTEL_IDMA64 is not set ++# CONFIG_MV_XOR_V2 is not set ++# CONFIG_PL330_DMA is not set ++# CONFIG_PLX_DMA is not set + CONFIG_TEGRA20_APB_DMA=y + CONFIG_TEGRA210_ADMA=m + CONFIG_TEGRA_GPC_DMA=y ++# CONFIG_XILINX_DMA is not set ++# CONFIG_XILINX_ZYNQMP_DMA is not set ++# CONFIG_XILINX_ZYNQMP_DPDMA is not set ++# CONFIG_QCOM_HIDMA_MGMT is not set ++# CONFIG_QCOM_HIDMA is not set ++# CONFIG_DW_DMAC is not set ++# CONFIG_DW_DMAC_PCI is not set ++# CONFIG_DW_EDMA is not set ++# CONFIG_DW_EDMA_PCIE is not set ++# CONFIG_SF_PDMA is not set ++ ++# ++# DMA Clients ++# ++# CONFIG_ASYNC_TX_DMA is not set + CONFIG_DMATEST=y ++CONFIG_DMA_ENGINE_RAID=y ++ ++# ++# DMABUF options ++# ++CONFIG_SYNC_FILE=y ++# CONFIG_SW_SYNC is not set ++# CONFIG_UDMABUF is not set ++# CONFIG_DMABUF_MOVE_NOTIFY is not set ++# CONFIG_DMABUF_SELFTESTS is not set ++CONFIG_DMABUF_DEFERRED_UNMAPPING=y + CONFIG_DMABUF_HEAPS=y + CONFIG_DMABUF_HEAPS_SYSTEM=y + CONFIG_DMABUF_HEAPS_CMA=y +-CONFIG_VFIO=m +-CONFIG_VFIO_PCI=m ++# end of DMABUF options ++ ++# CONFIG_AUXDISPLAY is not set ++CONFIG_UIO=m ++# CONFIG_UIO_CIF is not set ++# CONFIG_UIO_PDRV_GENIRQ is not set ++# CONFIG_UIO_DMEM_GENIRQ is not set ++# CONFIG_UIO_AEC is not set ++# CONFIG_UIO_SERCOS3 is not set ++# CONFIG_UIO_PCI_GENERIC is not set ++# CONFIG_UIO_NETX is not set ++# CONFIG_UIO_PRUSS is not set ++# CONFIG_UIO_MF624 is not set ++CONFIG_VFIO_IOMMU_TYPE1=y ++CONFIG_VFIO_VIRQFD=y ++CONFIG_VFIO=y ++# CONFIG_VFIO_NOIOMMU is not set ++CONFIG_VFIO_PCI=y ++CONFIG_VFIO_PCI_MMAP=y ++CONFIG_VFIO_PCI_INTX=y ++CONFIG_VFIO_PLATFORM=y ++# CONFIG_VFIO_AMBA is not set ++# CONFIG_VFIO_PLATFORM_CALXEDAXGMAC_RESET is not set ++# CONFIG_VFIO_PLATFORM_AMDXGBE_RESET is not set ++# CONFIG_VFIO_MDEV is not set + CONFIG_VIRT_DRIVERS=y +-CONFIG_VIRTIO_PCI=m ++CONFIG_VIRTIO=y ++CONFIG_VIRTIO_MENU=y ++CONFIG_VIRTIO_PCI=y ++CONFIG_VIRTIO_PCI_LEGACY=y + CONFIG_VIRTIO_BALLOON=m +-CONFIG_VIRTIO_MMIO=m ++# CONFIG_VIRTIO_INPUT is not set ++CONFIG_VIRTIO_MMIO=y ++# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set ++# CONFIG_VDPA is not set ++CONFIG_VHOST_MENU=y ++# CONFIG_VHOST_NET is not set ++# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# end of Microsoft Hyper-V guest support ++ ++# CONFIG_GREYBUS is not set ++CONFIG_STAGING=y + CONFIG_PRISM2_USB=m ++# CONFIG_COMEDI is not set + CONFIG_RTL8192U=m + CONFIG_RTLLIB=m ++CONFIG_RTLLIB_CRYPTO_CCMP=m ++CONFIG_RTLLIB_CRYPTO_TKIP=m ++CONFIG_RTLLIB_CRYPTO_WEP=m + CONFIG_RTL8192E=m ++# CONFIG_RTL8723BS is not set + CONFIG_R8712U=m + CONFIG_R8188EU=m ++CONFIG_88EU_AP_MODE=y ++# CONFIG_RTS5208 is not set ++# CONFIG_VT6655 is not set ++# CONFIG_VT6656 is not set ++ ++# ++# IIO staging drivers ++# ++ ++# ++# Accelerometers ++# ++# CONFIG_ADIS16203 is not set ++# CONFIG_ADIS16240 is not set ++# end of Accelerometers ++ ++# ++# Analog to digital converters ++# ++# CONFIG_AD7816 is not set ++# CONFIG_AD7280 is not set ++# end of Analog to digital converters ++ ++# ++# Analog digital bi-direction converters ++# ++# CONFIG_ADT7316 is not set ++# end of Analog digital bi-direction converters ++ ++# ++# Capacitance to digital converters ++# ++# CONFIG_AD7150 is not set ++# CONFIG_AD7746 is not set ++# end of Capacitance to digital converters ++ ++# ++# Direct Digital Synthesis ++# ++# CONFIG_AD9832 is not set ++# CONFIG_AD9834 is not set ++# end of Direct Digital Synthesis ++ ++# ++# Network Analyzer, Impedance Converters ++# ++# CONFIG_AD5933 is not set ++# end of Network Analyzer, Impedance Converters ++ ++# ++# Active energy metering IC ++# ++# CONFIG_ADE7854 is not set ++# CONFIG_INA219 is not set ++# CONFIG_INA230 is not set ++# CONFIG_INA3221 is not set ++# end of Active energy metering IC ++ ++# ++# Resolver to digital converters ++# ++# CONFIG_AD2S1210 is not set ++# end of Resolver to digital converters ++# end of IIO staging drivers ++ ++# CONFIG_FB_SM750 is not set ++# CONFIG_MFD_NVEC is not set ++# CONFIG_STAGING_MEDIA is not set ++ ++# ++# Android ++# ++# end of Android ++ ++# CONFIG_STAGING_BOARD is not set ++# CONFIG_LTE_GDM724X is not set ++# CONFIG_GS_FPGABOOT is not set ++# CONFIG_UNISYSSPAR is not set ++# CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set ++# CONFIG_FB_TFT is not set ++# CONFIG_KS7010 is not set ++# CONFIG_PI433 is not set ++ ++# ++# Gasket devices ++# ++# CONFIG_STAGING_GASKET_FRAMEWORK is not set ++# end of Gasket devices ++ ++# CONFIG_XIL_AXIS_FIFO is not set ++# CONFIG_FIELDBUS_DEV is not set ++# CONFIG_KPC2000 is not set + CONFIG_QLGE=m ++# CONFIG_WFX is not set ++# CONFIG_SPMI_HISI3670 is not set ++# CONFIG_MFD_HI6421_SPMI is not set ++# CONFIG_USB_WPAN_HCD is not set + CONFIG_TEGRA_HTS_GTE=y ++# CONFIG_TEGRA_GTE_TEST is not set ++# CONFIG_DUMMY_MEMORY_CARVEOUT is not set ++# CONFIG_GOLDFISH is not set ++# CONFIG_CHROME_PLATFORMS is not set ++# CONFIG_MELLANOX_PLATFORM is not set + CONFIG_DENVER_CPU=y ++# CONFIG_DENVER_MCA is not set + CONFIG_TEGRA_AON=y ++# CONFIG_TEGRA_ARI_MCA is not set ++# CONFIG_TEGRA_BRIDGE_MCA is not set ++# CONFIG_TEGRA_A57_SERR is not set + CONFIG_TEGRA_BWMGR=y + CONFIG_TEGRA_CAMERA_RTCPU=y + CONFIG_TEGRA_CAMERA_HSP_MBOX_CLIENT=y +@@ -1205,147 +6699,1444 @@ CONFIG_TEGRA_FSICOM=y + CONFIG_TEGRA_EPL=y + CONFIG_TEGRA_DCE=y + CONFIG_TEGRA_ISOMGR=y ++CONFIG_TEGRA_ISOMGR_POOL_KB_PER_SEC=0 + CONFIG_TEGRA_ISOMGR_SYSFS=y ++# CONFIG_TEGRA_ISOMGR_MAX_ISO_BW_QUIRK is not set ++CONFIG_NV_TEGRA_MC=y ++# CONFIG_TEGRA_VPR is not set ++CONFIG_TEGRA_MCE=y ++CONFIG_TEGRA_CACHE=y ++CONFIG_TEGRA_OF_MCERR=y ++# CONFIG_TEGRA_PM_IRQ is not set ++# CONFIG_TEGRA_PMC_AO_WAKE is not set ++# CONFIG_TEGRA_WAKEUP is not set ++CONFIG_TEGRA_PTP_NOTIFIER=y ++CONFIG_TEGRA_SOC_HWPM=y + CONFIG_TEGRA_SPE=y + CONFIG_TEGRA_SPE_HSP_MBOX_CLIENT=y ++# CONFIG_TEGRA_CBB_NOC is not set ++# CONFIG_TEGRA_HV_XHCI_DEBUG is not set ++# CONFIG_TEGRA_NVDUMPER is not set + CONFIG_TEGRA_CENTRAL_ACTMON=y ++CONFIG_TEGRA_FIRMWARES_CLASS=y ++CONFIG_TEGRA_FIRMWARES_INVENTORY=y ++# CONFIG_TEGRA_FIQ_DEBUGGER is not set + CONFIG_TEGRA_BOOTLOADER_DEBUG=m ++CONFIG_TEGRA_BOOTLOADER_DEBUG_INIT=y ++# CONFIG_TEGRA_BOOTLOADER_BOOT_CFG is not set ++CONFIG_NV_TEGRA_IVC=y ++# CONFIG_TEGRA_PM_DEBUG is not set + CONFIG_TEGRA_CLOCKS_CONFIGURE=y + CONFIG_TEGRA_USS_IO_PROXY=y ++CONFIG_TEGRA_CVNAS=y + CONFIG_TEGRA_SAFETY=y ++# CONFIG_TEGRA_SAFETY_IVC_DEBUG is not set ++# CONFIG_TEGRA_HSIERRRPTINJ is not set ++CONFIG_TEGRA_T234_HWPM=y + CONFIG_TEGRA_NVADSP=m + CONFIG_TEGRA_NVADSP_ON_SMMU=y ++# CONFIG_TEGRA_ADSP_DFS is not set ++# CONFIG_TEGRA_ADSP_CPUSTAT is not set + CONFIG_TEGRA_ADSP_FILEIO=y + CONFIG_TEGRA_ADSP_LPTHREAD=y ++# CONFIG_TEGRA_EMC_APE_DFS is not set ++CONFIG_TEGRA_ADSP_CONSOLE=y ++# CONFIG_MBOX_ACK_HANDLER is not set + CONFIG_TEGRA_VIRT_AUDIO_IVC=y ++CONFIG_HAVE_CLK=y ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++# CONFIG_COMMON_CLK_MAX77686 is not set ++# CONFIG_COMMON_CLK_MAX9485 is not set ++# CONFIG_COMMON_CLK_RK808 is not set ++# CONFIG_COMMON_CLK_SCPI is not set ++# CONFIG_COMMON_CLK_SI5341 is not set ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI514 is not set ++# CONFIG_COMMON_CLK_SI544 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_CDCE706 is not set ++# CONFIG_COMMON_CLK_CDCE925 is not set ++# CONFIG_COMMON_CLK_CS2000_CP is not set ++# CONFIG_COMMON_CLK_S2MPS11 is not set ++# CONFIG_CLK_QORIQ is not set ++# CONFIG_COMMON_CLK_XGENE is not set ++# CONFIG_COMMON_CLK_PWM is not set ++# CONFIG_COMMON_CLK_VC5 is not set ++# CONFIG_COMMON_CLK_BD718XX is not set ++# CONFIG_COMMON_CLK_FIXED_MMIO is not set + CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING=y ++# CONFIG_COMMON_CLK_BEGIN_ACCOUNTING_FROM_BOOT is not set ++CONFIG_CLK_TEGRA_BPMP=y ++CONFIG_TEGRA_CLK_DFLL=y ++CONFIG_TEGRA_CLK_DEBUG=y ++# CONFIG_HWSPINLOCK is not set ++ ++# ++# Clock Source drivers ++# ++CONFIG_TIMER_OF=y ++CONFIG_TIMER_ACPI=y ++CONFIG_TIMER_PROBE=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_TEGRA_TIMER=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND=y ++CONFIG_FSL_ERRATUM_A008585=y ++CONFIG_HISILICON_ERRATUM_161010101=y ++CONFIG_ARM64_ERRATUM_858921=y ++# CONFIG_MICROCHIP_PIT64B is not set ++# end of Clock Source drivers ++ + CONFIG_CLK_SRC_TEGRA18_TIMER=y ++CONFIG_MAILBOX=y + CONFIG_ARM_MHU=m ++# CONFIG_PLATFORM_MHU is not set ++# CONFIG_PL320_MBOX is not set ++CONFIG_PCC=y ++# CONFIG_ALTERA_MBOX is not set ++# CONFIG_MAILBOX_TEST is not set ++CONFIG_TEGRA_HSP_MBOX=y ++CONFIG_IOMMU_IOVA=y ++CONFIG_IOMMU_API=y ++CONFIG_IOMMU_SUPPORT=y ++ ++# ++# Generic IOMMU Pagetable Support ++# ++CONFIG_IOMMU_IO_PGTABLE=y ++CONFIG_IOMMU_IO_PGTABLE_LPAE=y ++# CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST is not set ++# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set ++# end of Generic IOMMU Pagetable Support ++ ++# CONFIG_IOMMU_DEBUGFS is not set ++# CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set ++CONFIG_OF_IOMMU=y ++CONFIG_IOMMU_DMA=y + CONFIG_TEGRA_IOMMU_SMMU=y + CONFIG_ARM_SMMU=y + CONFIG_ARM_SMMU_DEBUG=y ++# CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS is not set ++CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT=y + CONFIG_ARM_SMMU_V3=y ++# CONFIG_ARM_SMMU_V3_SVA is not set ++# CONFIG_VIRTIO_IOMMU is not set ++CONFIG_ARM_SMMU_SUSPEND=y ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_REMOTEPROC is not set ++# end of Remoteproc drivers ++ ++# ++# Rpmsg drivers ++# ++# CONFIG_RPMSG_QCOM_GLINK_RPM is not set ++# CONFIG_RPMSG_VIRTIO is not set ++# end of Rpmsg drivers ++ + CONFIG_SOUNDWIRE=m ++ ++# ++# SoundWire Devices ++# ++# CONFIG_SOUNDWIRE_INTEL is not set ++# CONFIG_SOUNDWIRE_QCOM is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++ ++# ++# Amlogic SoC drivers ++# ++# end of Amlogic SoC drivers ++ ++# ++# Aspeed SoC drivers ++# ++# end of Aspeed SoC drivers ++ ++# ++# Broadcom SoC drivers ++# ++# CONFIG_SOC_BRCMSTB is not set ++# end of Broadcom SoC drivers ++ ++# ++# NXP/Freescale QorIQ SoC drivers ++# ++# CONFIG_QUICC_ENGINE is not set ++# CONFIG_FSL_RCPM is not set ++# end of NXP/Freescale QorIQ SoC drivers ++ ++# ++# i.MX SoC drivers ++# ++# end of i.MX SoC drivers ++ ++# ++# Qualcomm SoC drivers ++# ++# end of Qualcomm SoC drivers ++ ++# CONFIG_ARCH_TEGRA_132_SOC is not set + CONFIG_ARCH_TEGRA_210_SOC=y + CONFIG_ARCH_TEGRA_186_SOC=y + CONFIG_ARCH_TEGRA_194_SOC=y + CONFIG_ARCH_TEGRA_234_SOC=y ++CONFIG_SOC_TEGRA_FUSE=y ++CONFIG_SOC_TEGRA_FLOWCTRL=y ++CONFIG_SOC_TEGRA_PMC=y ++CONFIG_SOC_TEGRA_POWERGATE_BPMP=y ++CONFIG_TEGRA_USE_NA_GPCPLL=y ++CONFIG_SOC_TEGRA_CBB=y + CONFIG_TEGRA_KFUSE=y ++# CONFIG_TEGRA_FUSE_DOWNSTREAM is not set + CONFIG_TEGRA_FUSE_BURN=y ++# CONFIG_TEGRA_PROC_POWER_MODEL is not set ++CONFIG_TEGRA_DVFS=y + CONFIG_TEGRA_210_DVFS=y ++# CONFIG_SOC_TI is not set ++ ++# ++# Xilinx SoC drivers ++# ++# CONFIG_XILINX_VCU is not set ++# end of Xilinx SoC drivers ++# end of SOC (System On Chip) specific Drivers ++ ++CONFIG_PM_DEVFREQ=y ++ ++# ++# DEVFREQ Governors ++# ++CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y + CONFIG_DEVFREQ_GOV_PERFORMANCE=y ++# CONFIG_DEVFREQ_GOV_POWERSAVE is not set + CONFIG_DEVFREQ_GOV_USERSPACE=y ++# CONFIG_DEVFREQ_GOV_PASSIVE is not set ++ ++# ++# DEVFREQ Drivers ++# ++# CONFIG_ARM_TEGRA_DEVFREQ is not set ++# CONFIG_PM_DEVFREQ_EVENT is not set ++ ++# ++# NVIDIA DEVFREQ Governors ++# ++# CONFIG_DEVFREQ_GOV_POD_SCALING is not set + CONFIG_DEVFREQ_GOV_POD_SCALING_V2=y ++CONFIG_DEVFREQ_GOV_POD_SCALING_HISTORY_BUFFER_SIZE_MAX=100 ++# CONFIG_DEVFREQ_GOV_WMARK_SIMPLE is not set + CONFIG_DEVFREQ_GOV_WMARK_ACTIVE=y ++CONFIG_EXTCON=y ++ ++# ++# Extcon Device Drivers ++# ++# CONFIG_EXTCON_ADC_JACK is not set ++# CONFIG_EXTCON_FSA9480 is not set + CONFIG_EXTCON_GPIO=y ++# CONFIG_EXTCON_MAX3355 is not set ++# CONFIG_EXTCON_CABLE_XLATE is not set ++# CONFIG_EXTCON_PTN5150 is not set ++# CONFIG_EXTCON_RT8973A is not set ++# CONFIG_EXTCON_SM5502 is not set + CONFIG_EXTCON_USB_GPIO=y ++CONFIG_EXTCON_DISP_STATE=y + CONFIG_MEMORY=y ++# CONFIG_ARM_PL172_MPMC is not set ++CONFIG_TEGRA_MC=y ++# CONFIG_TEGRA210_EMC is not set + CONFIG_IIO=y ++CONFIG_IIO_BUFFER=y ++# CONFIG_IIO_BUFFER_CB is not set ++# CONFIG_IIO_BUFFER_DMA is not set ++# CONFIG_IIO_BUFFER_DMAENGINE is not set ++# CONFIG_IIO_BUFFER_HW_CONSUMER is not set ++CONFIG_IIO_KFIFO_BUF=m ++CONFIG_IIO_TRIGGERED_BUFFER=m ++# CONFIG_IIO_CONFIGFS is not set ++CONFIG_IIO_TRIGGER=y ++CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 ++# CONFIG_IIO_SW_DEVICE is not set ++# CONFIG_IIO_SW_TRIGGER is not set ++# CONFIG_IIO_TRIGGERED_EVENT is not set ++ ++# ++# Accelerometers ++# ++# CONFIG_ADIS16201 is not set ++# CONFIG_ADIS16209 is not set ++# CONFIG_ADXL345_I2C is not set ++# CONFIG_ADXL345_SPI is not set ++# CONFIG_ADXL372_SPI is not set ++# CONFIG_ADXL372_I2C is not set ++# CONFIG_BMA180 is not set ++# CONFIG_BMA220 is not set ++# CONFIG_BMA400 is not set ++# CONFIG_BMC150_ACCEL is not set ++# CONFIG_DA280 is not set ++# CONFIG_DA311 is not set ++# CONFIG_DMARD06 is not set ++# CONFIG_DMARD09 is not set ++# CONFIG_DMARD10 is not set + CONFIG_HID_SENSOR_ACCEL_3D=m ++# CONFIG_IIO_ST_ACCEL_3AXIS is not set ++# CONFIG_KXSD9 is not set ++# CONFIG_KXCJK1013 is not set ++# CONFIG_MC3230 is not set ++# CONFIG_MMA7455_I2C is not set ++# CONFIG_MMA7455_SPI is not set ++# CONFIG_MMA7660 is not set ++# CONFIG_MMA8452 is not set ++# CONFIG_MMA9551 is not set ++# CONFIG_MMA9553 is not set ++# CONFIG_MXC4005 is not set ++# CONFIG_MXC6255 is not set ++# CONFIG_SCA3000 is not set ++# CONFIG_STK8312 is not set ++# CONFIG_STK8BA50 is not set ++# end of Accelerometers ++ ++# ++# Analog to digital converters ++# ++# CONFIG_AD7091R5 is not set ++# CONFIG_AD7124 is not set ++# CONFIG_AD7192 is not set ++# CONFIG_AD7266 is not set ++# CONFIG_AD7291 is not set ++# CONFIG_AD7292 is not set ++# CONFIG_AD7298 is not set ++# CONFIG_AD7476 is not set ++# CONFIG_AD7606_IFACE_PARALLEL is not set ++# CONFIG_AD7606_IFACE_SPI is not set ++# CONFIG_AD7766 is not set ++# CONFIG_AD7768_1 is not set ++# CONFIG_AD7780 is not set ++# CONFIG_AD7791 is not set ++# CONFIG_AD7793 is not set ++# CONFIG_AD7887 is not set ++# CONFIG_AD7923 is not set ++# CONFIG_AD7949 is not set ++# CONFIG_AD799X is not set ++# CONFIG_ADI_AXI_ADC is not set ++# CONFIG_CC10001_ADC is not set ++# CONFIG_ENVELOPE_DETECTOR is not set ++# CONFIG_HI8435 is not set ++# CONFIG_HX711 is not set ++# CONFIG_INA2XX_ADC is not set ++# CONFIG_LTC2471 is not set ++# CONFIG_LTC2485 is not set ++# CONFIG_LTC2496 is not set ++# CONFIG_LTC2497 is not set ++# CONFIG_MAX1027 is not set ++# CONFIG_MAX11100 is not set ++# CONFIG_MAX1118 is not set ++# CONFIG_MAX1241 is not set ++# CONFIG_MAX1363 is not set ++# CONFIG_MAX9611 is not set ++# CONFIG_MCP320X is not set ++# CONFIG_MCP3422 is not set ++# CONFIG_MCP3911 is not set ++# CONFIG_NAU7802 is not set ++CONFIG_QCOM_VADC_COMMON=m ++# CONFIG_QCOM_SPMI_IADC is not set ++# CONFIG_QCOM_SPMI_VADC is not set + CONFIG_QCOM_SPMI_ADC5=m ++# CONFIG_SD_ADC_MODULATOR is not set ++# CONFIG_TI_ADC081C is not set ++# CONFIG_TI_ADC0832 is not set ++# CONFIG_TI_ADC084S021 is not set ++# CONFIG_TI_ADC12138 is not set ++# CONFIG_TI_ADC108S102 is not set ++# CONFIG_TI_ADC128S052 is not set ++# CONFIG_TI_ADC161S626 is not set ++# CONFIG_TI_ADS1015 is not set ++# CONFIG_TI_ADS7950 is not set ++# CONFIG_TI_ADS8344 is not set ++# CONFIG_TI_ADS8688 is not set ++# CONFIG_TI_ADS124S08 is not set ++# CONFIG_TI_TLC4541 is not set ++# CONFIG_VF610_ADC is not set ++# CONFIG_XILINX_XADC is not set ++# end of Analog to digital converters ++ ++# ++# Analog Front Ends ++# ++# CONFIG_IIO_RESCALE is not set ++# end of Analog Front Ends ++ ++# ++# Amplifiers ++# ++# CONFIG_AD8366 is not set ++# CONFIG_HMC425 is not set ++# end of Amplifiers ++ ++# ++# Chemical Sensors ++# ++# CONFIG_ATLAS_PH_SENSOR is not set ++# CONFIG_ATLAS_EZO_SENSOR is not set ++# CONFIG_BME680 is not set ++# CONFIG_CCS811 is not set ++# CONFIG_IAQCORE is not set ++# CONFIG_PMS7003 is not set ++# CONFIG_SCD30_CORE is not set ++# CONFIG_SENSIRION_SGP30 is not set ++# CONFIG_SPS30 is not set ++# CONFIG_VZ89X is not set ++# end of Chemical Sensors ++ ++# ++# Hid Sensor IIO Common ++# ++CONFIG_HID_SENSOR_IIO_COMMON=m ++CONFIG_HID_SENSOR_IIO_TRIGGER=m ++# end of Hid Sensor IIO Common ++ ++# ++# SSP Sensor Common ++# ++# CONFIG_IIO_SSP_SENSORHUB is not set ++# end of SSP Sensor Common ++ ++# ++# Digital to analog converters ++# ++# CONFIG_AD5064 is not set ++# CONFIG_AD5360 is not set ++# CONFIG_AD5380 is not set ++# CONFIG_AD5421 is not set ++# CONFIG_AD5446 is not set ++# CONFIG_AD5449 is not set ++# CONFIG_AD5592R is not set ++# CONFIG_AD5593R is not set ++# CONFIG_AD5504 is not set ++# CONFIG_AD5624R_SPI is not set ++# CONFIG_AD5686_SPI is not set ++# CONFIG_AD5696_I2C is not set ++# CONFIG_AD5755 is not set ++# CONFIG_AD5758 is not set ++# CONFIG_AD5761 is not set ++# CONFIG_AD5764 is not set ++# CONFIG_AD5770R is not set ++# CONFIG_AD5791 is not set ++# CONFIG_AD7303 is not set ++# CONFIG_AD8801 is not set ++# CONFIG_DPOT_DAC is not set + CONFIG_DS4424=m ++# CONFIG_LTC1660 is not set ++# CONFIG_LTC2632 is not set ++# CONFIG_M62332 is not set ++# CONFIG_MAX517 is not set ++# CONFIG_MAX5821 is not set ++# CONFIG_MCP4725 is not set ++# CONFIG_MCP4922 is not set ++# CONFIG_TI_DAC082S085 is not set ++# CONFIG_TI_DAC5571 is not set ++# CONFIG_TI_DAC7311 is not set ++# CONFIG_TI_DAC7612 is not set ++# CONFIG_VF610_DAC is not set ++# end of Digital to analog converters ++ ++# ++# IIO dummy driver ++# ++# end of IIO dummy driver ++ ++# ++# Frequency Synthesizers DDS/PLL ++# ++ ++# ++# Clock Generator/Distribution ++# ++# CONFIG_AD9523 is not set ++# end of Clock Generator/Distribution ++ ++# ++# Phase-Locked Loop (PLL) frequency synthesizers ++# ++# CONFIG_ADF4350 is not set ++# CONFIG_ADF4371 is not set ++# end of Phase-Locked Loop (PLL) frequency synthesizers ++# end of Frequency Synthesizers DDS/PLL ++ ++# ++# Digital gyroscope sensors ++# ++# CONFIG_ADIS16080 is not set ++# CONFIG_ADIS16130 is not set ++# CONFIG_ADIS16136 is not set ++# CONFIG_ADIS16260 is not set ++# CONFIG_ADXRS290 is not set ++# CONFIG_ADXRS450 is not set ++# CONFIG_BMG160 is not set ++# CONFIG_FXAS21002C is not set + CONFIG_HID_SENSOR_GYRO_3D=m ++# CONFIG_MPU3050_I2C is not set ++# CONFIG_IIO_ST_GYRO_3AXIS is not set ++# CONFIG_ITG3200 is not set ++# end of Digital gyroscope sensors ++ ++# ++# Health Sensors ++# ++ ++# ++# Heart Rate Monitors ++# ++# CONFIG_AFE4403 is not set ++# CONFIG_AFE4404 is not set ++# CONFIG_MAX30100 is not set ++# CONFIG_MAX30102 is not set ++# end of Heart Rate Monitors ++# end of Health Sensors ++ ++# ++# Humidity sensors ++# ++# CONFIG_AM2315 is not set ++# CONFIG_DHT11 is not set ++# CONFIG_HDC100X is not set ++# CONFIG_HDC2010 is not set ++# CONFIG_HID_SENSOR_HUMIDITY is not set ++# CONFIG_HTS221 is not set ++# CONFIG_HTU21 is not set ++# CONFIG_SI7005 is not set ++# CONFIG_SI7020 is not set ++# end of Humidity sensors ++ ++# ++# Inertial measurement units ++# ++# CONFIG_ADIS16400 is not set ++# CONFIG_ADIS16460 is not set ++# CONFIG_ADIS16475 is not set ++# CONFIG_ADIS16480 is not set ++# CONFIG_BMI160_I2C is not set ++# CONFIG_BMI160_SPI is not set ++# CONFIG_FXOS8700_I2C is not set ++# CONFIG_FXOS8700_SPI is not set ++# CONFIG_KMX61 is not set ++# CONFIG_INV_ICM42600_I2C is not set ++# CONFIG_INV_ICM42600_SPI is not set ++# CONFIG_INV_MPU6050_I2C is not set ++# CONFIG_INV_MPU6050_SPI is not set ++# CONFIG_IIO_ST_LSM6DSX is not set ++# CONFIG_NVI_MPU_IIO is not set ++# CONFIG_NVI_MPU_INPUT is not set ++# CONFIG_NVI_MPU_RELAY is not set ++# CONFIG_NVS_BMI160_IIO is not set ++# CONFIG_NVS_BMI160_INPUT is not set ++# CONFIG_NVS_BMI160_RELAY is not set ++# CONFIG_NVS_BMI08X_IIO is not set ++# CONFIG_NVS_BMI08X_INPUT is not set ++# CONFIG_NVS_BMI08X_RELAY is not set + CONFIG_BMI088_IIO=m ++# CONFIG_TSFW_ICM is not set ++# end of Inertial measurement units ++ ++# ++# Light sensors ++# ++# CONFIG_ACPI_ALS is not set ++# CONFIG_ADJD_S311 is not set ++# CONFIG_ADUX1020 is not set ++# CONFIG_AL3010 is not set ++# CONFIG_AL3320A is not set ++# CONFIG_APDS9300 is not set ++# CONFIG_APDS9960 is not set ++# CONFIG_AS73211 is not set ++# CONFIG_BH1750 is not set ++# CONFIG_BH1780 is not set ++# CONFIG_CM32181 is not set ++# CONFIG_CM3232 is not set ++# CONFIG_CM3323 is not set ++# CONFIG_CM3605 is not set ++# CONFIG_CM36651 is not set ++# CONFIG_GP2AP002 is not set ++# CONFIG_GP2AP020A00F is not set ++# CONFIG_SENSORS_ISL29018 is not set ++# CONFIG_SENSORS_ISL29028 is not set ++# CONFIG_ISL29125 is not set ++# CONFIG_HID_SENSOR_ALS is not set ++# CONFIG_HID_SENSOR_PROX is not set ++# CONFIG_JSA1212 is not set ++# CONFIG_RPR0521 is not set ++# CONFIG_LTR501 is not set ++# CONFIG_LV0104CS is not set ++# CONFIG_MAX44000 is not set ++# CONFIG_MAX44009 is not set ++# CONFIG_NOA1305 is not set ++# CONFIG_OPT3001 is not set ++# CONFIG_PA12203001 is not set ++# CONFIG_SI1133 is not set ++# CONFIG_SI1145 is not set ++# CONFIG_STK3310 is not set ++# CONFIG_ST_UVIS25 is not set ++# CONFIG_TCS3414 is not set ++# CONFIG_TCS3472 is not set ++# CONFIG_SENSORS_TSL2563 is not set ++# CONFIG_TSL2583 is not set ++# CONFIG_TSL2772 is not set ++# CONFIG_TSL4531 is not set ++# CONFIG_US5182D is not set ++# CONFIG_VCNL4000 is not set ++# CONFIG_VCNL4035 is not set ++# CONFIG_VEML6030 is not set ++# CONFIG_VEML6070 is not set ++# CONFIG_VL6180 is not set ++# CONFIG_ZOPT2201 is not set ++# end of Light sensors ++ ++# ++# Magnetometer sensors ++# ++# CONFIG_AK8974 is not set ++# CONFIG_AK8975 is not set ++# CONFIG_AK09911 is not set ++# CONFIG_BMC150_MAGN_I2C is not set ++# CONFIG_BMC150_MAGN_SPI is not set ++# CONFIG_MAG3110 is not set ++# CONFIG_HID_SENSOR_MAGNETOMETER_3D is not set ++# CONFIG_MMC35240 is not set ++# CONFIG_IIO_ST_MAGN_3AXIS is not set ++# CONFIG_SENSORS_HMC5843_I2C is not set ++# CONFIG_SENSORS_HMC5843_SPI is not set ++# CONFIG_SENSORS_RM3100_I2C is not set ++# CONFIG_SENSORS_RM3100_SPI is not set ++# end of Magnetometer sensors ++ ++# ++# Multiplexers ++# ++# CONFIG_IIO_MUX is not set ++# end of Multiplexers ++ ++# ++# Inclinometer sensors ++# ++# CONFIG_HID_SENSOR_INCLINOMETER_3D is not set ++# CONFIG_HID_SENSOR_DEVICE_ROTATION is not set ++# end of Inclinometer sensors ++ ++# ++# Triggers - standalone ++# ++# CONFIG_IIO_INTERRUPT_TRIGGER is not set ++# CONFIG_IIO_SYSFS_TRIGGER is not set ++# end of Triggers - standalone ++ ++# ++# Linear and angular position sensors ++# ++# end of Linear and angular position sensors ++ ++# ++# Digital potentiometers ++# ++# CONFIG_AD5272 is not set ++# CONFIG_DS1803 is not set ++# CONFIG_MAX5432 is not set ++# CONFIG_MAX5481 is not set ++# CONFIG_MAX5487 is not set ++# CONFIG_MCP4018 is not set ++# CONFIG_MCP4131 is not set ++# CONFIG_MCP4531 is not set ++# CONFIG_MCP41010 is not set ++# CONFIG_TPL0102 is not set ++# end of Digital potentiometers ++ ++# ++# Digital potentiostats ++# ++# CONFIG_LMP91000 is not set ++# end of Digital potentiostats ++ ++# ++# Pressure sensors ++# ++# CONFIG_ABP060MG is not set ++# CONFIG_BMP280 is not set ++# CONFIG_DLHL60D is not set ++# CONFIG_DPS310 is not set ++# CONFIG_HID_SENSOR_PRESS is not set ++# CONFIG_HP03 is not set ++# CONFIG_ICP10100 is not set ++# CONFIG_MPL115_I2C is not set ++# CONFIG_MPL115_SPI is not set ++# CONFIG_MPL3115 is not set ++# CONFIG_MS5611 is not set ++# CONFIG_MS5637 is not set ++# CONFIG_IIO_ST_PRESS is not set ++# CONFIG_T5403 is not set ++# CONFIG_HP206C is not set ++# CONFIG_ZPA2326 is not set ++# end of Pressure sensors ++ ++# ++# Lightning sensors ++# ++# CONFIG_AS3935 is not set ++# end of Lightning sensors ++ ++# ++# Proximity and distance sensors ++# ++# CONFIG_ISL29501 is not set ++# CONFIG_LIDAR_LITE_V2 is not set ++# CONFIG_MB1232 is not set ++# CONFIG_PING is not set ++# CONFIG_RFD77402 is not set ++# CONFIG_SRF04 is not set ++# CONFIG_SX9310 is not set ++# CONFIG_SX9500 is not set ++# CONFIG_SRF08 is not set ++# CONFIG_VCNL3020 is not set ++# CONFIG_VL53L0X_I2C is not set ++# end of Proximity and distance sensors ++ ++# ++# Proximity sensors ++# ++# end of Proximity sensors ++ ++# ++# Resolver to digital converters ++# ++# CONFIG_AD2S90 is not set ++# CONFIG_AD2S1200 is not set ++# end of Resolver to digital converters ++ ++# ++# Temperature sensors ++# ++# CONFIG_LTC2983 is not set ++# CONFIG_MAXIM_THERMOCOUPLE is not set ++# CONFIG_HID_SENSOR_TEMP is not set ++# CONFIG_MLX90614 is not set ++# CONFIG_MLX90632 is not set ++# CONFIG_TMP006 is not set ++# CONFIG_TMP007 is not set ++# CONFIG_TSYS01 is not set ++# CONFIG_TSYS02D is not set ++# CONFIG_MAX31856 is not set ++# CONFIG_NVS_TMP1X2_IIO is not set ++# CONFIG_NVS_TMP1X2_INPUT is not set ++# CONFIG_NVS_TMP1X2_RELAY is not set ++# end of Temperature sensors ++ ++# CONFIG_NVS_LED_TEST is not set + CONFIG_NTB=m ++# CONFIG_NTB_MSI is not set ++# CONFIG_NTB_IDT is not set + CONFIG_NTB_SWITCHTEC=m + CONFIG_NTB_PINGPONG=m + CONFIG_NTB_TOOL=m + CONFIG_NTB_PERF=m + CONFIG_NTB_TRANSPORT=m ++# CONFIG_VME_BUS is not set + CONFIG_PWM=y ++CONFIG_PWM_SYSFS=y ++# CONFIG_PWM_DEBUG is not set ++# CONFIG_PWM_FSL_FTM is not set ++# CONFIG_PWM_PCA9685 is not set + CONFIG_PWM_TEGRA=y ++# CONFIG_PWM_TEGRA_PMC_SOFT_LED_BLINK is not set + CONFIG_PWM_TEGRA_PMC_BLINK=m + CONFIG_PWM_TEGRA_TACHOMETER=y + CONFIG_PWM_TEGRA_DFLL=y ++ ++# ++# IRQ chip support ++# ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++CONFIG_FIQ=y ++CONFIG_ARM_GIC_PM=y ++CONFIG_ARM_GIC_MAX_NR=1 ++CONFIG_ARM_GIC_V2M=y ++CONFIG_ARM_GIC_V3=y ++CONFIG_ARM_GIC_V3_ITS=y ++CONFIG_ARM_GIC_V3_ITS_PCI=y ++# CONFIG_AL_FIC is not set ++CONFIG_PARTITION_PERCPU=y ++# end of IRQ chip support ++ ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_RESET_TI_SYSCON is not set ++CONFIG_RESET_TEGRA_BPMP=y ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_PHY_XGENE is not set ++# CONFIG_BCM_KONA_USB2_PHY is not set ++# CONFIG_PHY_CADENCE_TORRENT is not set ++# CONFIG_PHY_CADENCE_DPHY is not set ++# CONFIG_PHY_CADENCE_SIERRA is not set ++# CONFIG_PHY_CADENCE_SALVO is not set + CONFIG_PHY_FSL_IMX8MQ_USB=y ++# CONFIG_PHY_MIXEL_MIPI_DPHY is not set ++# CONFIG_PHY_PXA_28NM_HSIC is not set ++# CONFIG_PHY_PXA_28NM_USB2 is not set ++# CONFIG_PHY_CPCAP_USB is not set ++# CONFIG_PHY_MAPPHONE_MDM6600 is not set ++# CONFIG_PHY_OCELOT_SERDES is not set ++# CONFIG_PHY_QCOM_USB_HS is not set ++# CONFIG_PHY_QCOM_USB_HSIC is not set + CONFIG_PHY_TEGRA_XUSB=y ++CONFIG_PHY_TEGRA194_P2U=y ++# CONFIG_TEGRA_P2U is not set ++# CONFIG_PHY_TUSB1210 is not set ++# end of PHY Subsystem ++ ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++ ++# ++# Performance monitor support ++# ++# CONFIG_ARM_CCI_PMU is not set ++# CONFIG_ARM_CCN is not set ++# CONFIG_ARM_CMN is not set ++CONFIG_ARM_PMU=y ++CONFIG_ARM_PMU_ACPI=y + CONFIG_ARM_SMMU_V3_PMU=m + CONFIG_ARM_DSU_PMU=y + CONFIG_ARM_SPE_PMU=y ++# CONFIG_HISI_PMU is not set ++# end of Performance monitor support ++ ++CONFIG_RAS=y ++CONFIG_ARM64_RAS=y ++# CONFIG_USB4 is not set ++ ++# ++# Android ++# ++# CONFIG_ANDROID is not set ++# end of Android ++ ++# CONFIG_LIBNVDIMM is not set + CONFIG_DAX=y ++# CONFIG_DEV_DAX is not set ++CONFIG_NVMEM=y ++CONFIG_NVMEM_SYSFS=y ++# CONFIG_NVMEM_SPMI_SDAM is not set ++ ++# ++# HW tracing support ++# ++# CONFIG_STM is not set ++# CONFIG_INTEL_TH is not set ++# end of HW tracing support ++ + CONFIG_FPGA=y ++# CONFIG_ALTERA_PR_IP_CORE is not set ++# CONFIG_FPGA_MGR_ALTERA_PS_SPI is not set ++# CONFIG_FPGA_MGR_ALTERA_CVP is not set ++# CONFIG_FPGA_MGR_XILINX_SPI is not set ++# CONFIG_FPGA_MGR_ICE40_SPI is not set ++# CONFIG_FPGA_MGR_MACHXO2_SPI is not set + CONFIG_FPGA_BRIDGE=m ++# CONFIG_ALTERA_FREEZE_BRIDGE is not set ++# CONFIG_XILINX_PR_DECOUPLER is not set + CONFIG_FPGA_REGION=m + CONFIG_OF_FPGA_REGION=m ++# CONFIG_FPGA_DFL is not set ++# CONFIG_FSI is not set + CONFIG_TEE=y ++ ++# ++# TEE drivers ++# + CONFIG_OPTEE=y ++CONFIG_OPTEE_SHM_NUM_PRIV_PAGES=1 ++# end of TEE drivers ++ ++CONFIG_PM_OPP=y ++# CONFIG_SIOX is not set ++# CONFIG_SLIMBUS is not set + CONFIG_INTERCONNECT=y + CONFIG_INTERCONNECT_TEGRA=y ++# CONFIG_COUNTER is not set ++# CONFIG_MOST is not set + CONFIG_RTK_BTUSB=m + CONFIG_NVPMODEL_EMC=y + CONFIG_TEGRA_RDMA=m ++ ++# ++# Generic IOMMU Pagetable Support ++# ++# end of Generic IOMMU Pagetable Support ++ + CONFIG_NVPPS=m ++ ++# ++# Trusty ++# ++# CONFIG_TRUSTY is not set ++# end of Trusty ++ + CONFIG_TEGRA_HV_PM_CTL=y + CONFIG_TEGRA_HV_MANAGER=y + CONFIG_TEGRA_VIRTUALIZATION=y ++CONFIG_TEGRA_GPIO_HOST_PROXY=y ++CONFIG_TEGRA_GPIO_GUEST_PROXY=y ++# end of Device Drivers ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_VALIDATE_FS_PARSER is not set ++CONFIG_FS_IOMAP=y ++# CONFIG_EXT2_FS is not set + CONFIG_EXT3_FS=y ++# CONFIG_EXT3_FS_POSIX_ACL is not set ++# CONFIG_EXT3_FS_SECURITY is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT2=y + CONFIG_EXT4_FS_POSIX_ACL=y + CONFIG_EXT4_FS_SECURITY=y ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set + CONFIG_BTRFS_FS=m + CONFIG_BTRFS_FS_POSIX_ACL=y ++# CONFIG_BTRFS_FS_CHECK_INTEGRITY is not set ++# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set ++# CONFIG_BTRFS_DEBUG is not set ++# CONFIG_BTRFS_ASSERT is not set ++# CONFIG_BTRFS_FS_REF_VERIFY is not set ++# CONFIG_NILFS2_FS is not set ++# CONFIG_F2FS_FS is not set ++# CONFIG_FS_DAX is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_EXPORTFS=y ++# CONFIG_EXPORTFS_BLOCK_OPS is not set ++CONFIG_FILE_LOCKING=y ++CONFIG_MANDATORY_FILE_LOCKING=y ++# CONFIG_FS_ENCRYPTION is not set ++# CONFIG_FS_VERITY is not set ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y + CONFIG_FANOTIFY=y + CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y + CONFIG_QUOTA=y + CONFIG_QUOTA_NETLINK_INTERFACE=y ++CONFIG_PRINT_QUOTA_WARNING=y ++# CONFIG_QUOTA_DEBUG is not set ++CONFIG_QUOTA_TREE=m ++# CONFIG_QFMT_V1 is not set + CONFIG_QFMT_V2=m ++CONFIG_QUOTACTL=y + CONFIG_AUTOFS4_FS=y ++CONFIG_AUTOFS_FS=y + CONFIG_FUSE_FS=m + CONFIG_CUSE=m +-CONFIG_ISO9660_FS=y ++# CONFIG_VIRTIO_FS is not set + CONFIG_OVERLAY_FS=m ++# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set + # CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW is not set ++# CONFIG_OVERLAY_FS_INDEX is not set ++# CONFIG_OVERLAY_FS_XINO_AUTO is not set ++# CONFIG_OVERLAY_FS_METACOPY is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++# end of Caches ++ ++# ++# CD-ROM/DVD Filesystems ++# ++CONFIG_ISO9660_FS=y ++# CONFIG_JOLIET is not set ++# CONFIG_ZISOFS is not set ++# CONFIG_UDF_FS is not set ++# end of CD-ROM/DVD Filesystems ++ ++# ++# DOS/FAT/EXFAT/NT Filesystems ++# ++CONFIG_FAT_FS=y + CONFIG_MSDOS_FS=y + CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_FAT_DEFAULT_UTF8 is not set + CONFIG_EXFAT_FS=y ++CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" + CONFIG_NTFS_FS=y ++# CONFIG_NTFS_DEBUG is not set + CONFIG_NTFS_RW=y ++# end of DOS/FAT/EXFAT/NT Filesystems ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++# CONFIG_PROC_KCORE is not set ++CONFIG_PROC_VMCORE=y ++# CONFIG_PROC_VMCORE_DEVICE_DUMP is not set ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++# CONFIG_PROC_CHILDREN is not set ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y + CONFIG_TMPFS=y + CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_TMPFS_XATTR=y ++# CONFIG_TMPFS_INODE64 is not set + CONFIG_HUGETLBFS=y ++CONFIG_HUGETLB_PAGE=y ++CONFIG_MEMFD_CREATE=y ++CONFIG_ARCH_HAS_GIGANTIC_PAGE=y ++CONFIG_CONFIGFS_FS=y + CONFIG_EFIVAR_FS=y ++# end of Pseudo filesystems ++ ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ORANGEFS_FS is not set ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_ECRYPT_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++# CONFIG_UBIFS_FS is not set ++# CONFIG_CRAMFS is not set + CONFIG_SQUASHFS=y ++CONFIG_SQUASHFS_FILE_CACHE=y ++# CONFIG_SQUASHFS_FILE_DIRECT is not set ++CONFIG_SQUASHFS_DECOMP_SINGLE=y ++# CONFIG_SQUASHFS_DECOMP_MULTI is not set ++# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set + CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_ZLIB=y + CONFIG_SQUASHFS_LZ4=y + CONFIG_SQUASHFS_LZO=y + CONFIG_SQUASHFS_XZ=y ++# CONFIG_SQUASHFS_ZSTD is not set ++# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set ++# CONFIG_SQUASHFS_EMBEDDED is not set ++CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++CONFIG_PSTORE=y ++CONFIG_PSTORE_DEFLATE_COMPRESS=y ++# CONFIG_PSTORE_LZO_COMPRESS is not set ++# CONFIG_PSTORE_LZ4_COMPRESS is not set ++# CONFIG_PSTORE_LZ4HC_COMPRESS is not set ++# CONFIG_PSTORE_842_COMPRESS is not set ++# CONFIG_PSTORE_ZSTD_COMPRESS is not set ++CONFIG_PSTORE_COMPRESS=y ++CONFIG_PSTORE_DEFLATE_COMPRESS_DEFAULT=y ++CONFIG_PSTORE_COMPRESS_DEFAULT="deflate" + CONFIG_PSTORE_CONSOLE=y ++# CONFIG_PSTORE_PMSG is not set ++# CONFIG_PSTORE_FTRACE is not set + CONFIG_PSTORE_RAM=m ++CONFIG_PSTORE_ZONE=y ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_EROFS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y + CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y + CONFIG_NFS_V3_ACL=y + CONFIG_NFS_V4=y ++# CONFIG_NFS_SWAP is not set + CONFIG_NFS_V4_1=y + CONFIG_NFS_V4_2=y ++CONFIG_PNFS_FILE_LAYOUT=y ++CONFIG_PNFS_BLOCK=y ++CONFIG_PNFS_FLEXFILE_LAYOUT=y ++CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="kernel.org" ++# CONFIG_NFS_V4_1_MIGRATION is not set ++CONFIG_NFS_V4_SECURITY_LABEL=y + CONFIG_ROOT_NFS=y ++# CONFIG_NFS_USE_LEGACY_DNS is not set ++CONFIG_NFS_USE_KERNEL_DNS=y + # CONFIG_NFS_DISABLE_UDP_SUPPORT is not set ++# CONFIG_NFS_V4_2_READ_PLUS is not set + CONFIG_NFSD=m ++CONFIG_NFSD_V2_ACL=y + CONFIG_NFSD_V3=y + CONFIG_NFSD_V3_ACL=y ++# CONFIG_NFSD_V4 is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++CONFIG_SUNRPC_GSS=y ++CONFIG_SUNRPC_BACKCHANNEL=y ++# CONFIG_SUNRPC_DEBUG is not set ++CONFIG_SUNRPC_XPRT_RDMA=m ++# CONFIG_CEPH_FS is not set + CONFIG_CIFS=m ++# CONFIG_CIFS_STATS2 is not set ++CONFIG_CIFS_ALLOW_INSECURE_LEGACY=y ++# CONFIG_CIFS_WEAK_PW_HASH is not set ++# CONFIG_CIFS_UPCALL is not set ++# CONFIG_CIFS_XATTR is not set ++CONFIG_CIFS_DEBUG=y ++# CONFIG_CIFS_DEBUG2 is not set ++# CONFIG_CIFS_DEBUG_DUMP_KEYS is not set ++# CONFIG_CIFS_DFS_UPCALL is not set ++# CONFIG_CIFS_SMB_DIRECT is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set + CONFIG_9P_FS=m ++# CONFIG_9P_FS_POSIX_ACL is not set ++# CONFIG_9P_FS_SECURITY is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" + CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set + CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set + CONFIG_NLS_UTF8=m ++# CONFIG_DLM is not set ++# CONFIG_UNICODE is not set ++CONFIG_IO_WQ=y ++# end of File systems ++ ++# ++# Security options ++# ++CONFIG_KEYS=y ++# CONFIG_KEYS_REQUEST_CACHE is not set ++# CONFIG_PERSISTENT_KEYRINGS is not set ++# CONFIG_ENCRYPTED_KEYS is not set ++# CONFIG_KEY_DH_OPERATIONS is not set ++# CONFIG_KEY_NOTIFICATIONS is not set + CONFIG_SECURITY_DMESG_RESTRICT=y + CONFIG_SECURITY=y ++# CONFIG_SECURITYFS is not set + CONFIG_SECURITY_NETWORK=y ++# CONFIG_SECURITY_INFINIBAND is not set ++# CONFIG_SECURITY_NETWORK_XFRM is not set ++# CONFIG_SECURITY_PATH is not set ++CONFIG_LSM_MMAP_MIN_ADDR=32768 ++CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y + CONFIG_HARDENED_USERCOPY=y ++CONFIG_HARDENED_USERCOPY_FALLBACK=y ++# CONFIG_HARDENED_USERCOPY_PAGESPAN is not set + CONFIG_FORTIFY_SOURCE=y ++# CONFIG_STATIC_USERMODEHELPER is not set + CONFIG_SECURITY_SELINUX=y ++# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set ++# CONFIG_SECURITY_SELINUX_DISABLE is not set ++CONFIG_SECURITY_SELINUX_DEVELOP=y ++CONFIG_SECURITY_SELINUX_AVC_STATS=y ++CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=0 ++CONFIG_SECURITY_SELINUX_SIDTAB_HASH_BITS=9 ++CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE=256 ++# CONFIG_SECURITY_SMACK is not set ++# CONFIG_SECURITY_TOMOYO is not set ++# CONFIG_SECURITY_APPARMOR is not set ++# CONFIG_SECURITY_LOADPIN is not set + CONFIG_SECURITY_YAMA=y ++# CONFIG_SECURITY_SAFESETID is not set ++# CONFIG_SECURITY_LOCKDOWN_LSM is not set ++CONFIG_INTEGRITY=y ++# CONFIG_INTEGRITY_SIGNATURE is not set ++CONFIG_INTEGRITY_AUDIT=y ++# CONFIG_IMA is not set ++# CONFIG_EVM is not set ++CONFIG_DEFAULT_SECURITY_SELINUX=y ++# CONFIG_DEFAULT_SECURITY_DAC is not set + CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity,selinux,bpf" ++ ++# ++# Kernel hardening options ++# ++ ++# ++# Memory initialization ++# ++CONFIG_INIT_STACK_NONE=y ++# CONFIG_GCC_PLUGIN_STRUCTLEAK_USER is not set ++# CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF is not set ++# CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL is not set ++# CONFIG_GCC_PLUGIN_STACKLEAK is not set ++# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set ++# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set ++# end of Memory initialization ++# end of Kernel hardening options ++ ++# CONFIG_TRUSTED_LITTLE_KERNEL is not set ++# end of Security options ++ ++CONFIG_XOR_BLOCKS=m ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=y ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_SKCIPHER=y ++CONFIG_CRYPTO_SKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_RNG_DEFAULT=y ++CONFIG_CRYPTO_AKCIPHER2=y ++CONFIG_CRYPTO_AKCIPHER=y ++CONFIG_CRYPTO_KPP2=y ++CONFIG_CRYPTO_KPP=y ++CONFIG_CRYPTO_ACOMP2=y ++CONFIG_CRYPTO_ECDSA=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++# CONFIG_CRYPTO_USER is not set ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++CONFIG_CRYPTO_GF128MUL=y ++CONFIG_CRYPTO_NULL=y ++CONFIG_CRYPTO_NULL2=y ++# CONFIG_CRYPTO_PCRYPT is not set ++CONFIG_CRYPTO_CRYPTD=m ++CONFIG_CRYPTO_AUTHENC=y + CONFIG_CRYPTO_TEST=m ++CONFIG_CRYPTO_SIMD=m ++CONFIG_CRYPTO_ENGINE=m ++ ++# ++# Public-key cryptography ++# ++CONFIG_CRYPTO_RSA=y + CONFIG_CRYPTO_DH=y ++CONFIG_CRYPTO_ECC=y ++CONFIG_CRYPTO_ECDH=y ++# CONFIG_CRYPTO_ECRDSA is not set ++# CONFIG_CRYPTO_SM2 is not set ++# CONFIG_CRYPTO_CURVE25519 is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++CONFIG_CRYPTO_CCM=m ++CONFIG_CRYPTO_GCM=m ++# CONFIG_CRYPTO_CHACHA20POLY1305 is not set ++# CONFIG_CRYPTO_AEGIS128 is not set + CONFIG_CRYPTO_SEQIV=y + CONFIG_CRYPTO_ECHAINIV=y ++ ++# ++# Block modes ++# ++CONFIG_CRYPTO_CBC=y ++# CONFIG_CRYPTO_CFB is not set + CONFIG_CRYPTO_CTR=y ++# CONFIG_CRYPTO_CTS is not set ++CONFIG_CRYPTO_ECB=y ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_OFB is not set ++# CONFIG_CRYPTO_PCBC is not set ++CONFIG_CRYPTO_XTS=m ++# CONFIG_CRYPTO_KEYWRAP is not set ++# CONFIG_CRYPTO_ADIANTUM is not set ++CONFIG_CRYPTO_ESSIV=y ++ ++# ++# Hash modes ++# ++CONFIG_CRYPTO_CMAC=y ++CONFIG_CRYPTO_HMAC=y ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++CONFIG_CRYPTO_XXHASH=m ++CONFIG_CRYPTO_BLAKE2B=m ++# CONFIG_CRYPTO_BLAKE2S is not set ++CONFIG_CRYPTO_CRCT10DIF=y + CONFIG_CRYPTO_GHASH=y ++# CONFIG_CRYPTO_POLY1305 is not set ++CONFIG_CRYPTO_MD4=m ++CONFIG_CRYPTO_MD5=m ++CONFIG_CRYPTO_MICHAEL_MIC=m ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++CONFIG_CRYPTO_SHA1=y ++CONFIG_CRYPTO_SHA256=y ++CONFIG_CRYPTO_SHA512=y ++CONFIG_CRYPTO_SHA3=m ++CONFIG_CRYPTO_SM3=m ++# CONFIG_CRYPTO_STREEBOG is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_TI is not set ++# CONFIG_CRYPTO_ANUBIS is not set + CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set + CONFIG_CRYPTO_DES=m ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_CHACHA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++CONFIG_CRYPTO_SM4=m ++# CONFIG_CRYPTO_TEA is not set + CONFIG_CRYPTO_TWOFISH=m ++CONFIG_CRYPTO_TWOFISH_COMMON=m ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++CONFIG_CRYPTO_LZO=m ++# CONFIG_CRYPTO_842 is not set ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++# CONFIG_CRYPTO_ZSTD is not set ++ ++# ++# Random Number Generation ++# + CONFIG_CRYPTO_ANSI_CPRNG=m ++CONFIG_CRYPTO_DRBG_MENU=y ++CONFIG_CRYPTO_DRBG_HMAC=y ++# CONFIG_CRYPTO_DRBG_HASH is not set ++# CONFIG_CRYPTO_DRBG_CTR is not set ++CONFIG_CRYPTO_DRBG=y ++CONFIG_CRYPTO_JITTERENTROPY=y ++CONFIG_CRYPTO_USER_API=y ++# CONFIG_CRYPTO_USER_API_HASH is not set + CONFIG_CRYPTO_USER_API_SKCIPHER=y + CONFIG_CRYPTO_USER_API_RNG=m ++# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set ++# CONFIG_CRYPTO_USER_API_AEAD is not set ++CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE=y ++CONFIG_CRYPTO_HASH_INFO=y ++ ++# ++# Crypto library routines ++# ++CONFIG_CRYPTO_LIB_AES=y ++CONFIG_CRYPTO_LIB_ARC4=y ++# CONFIG_CRYPTO_LIB_BLAKE2S is not set ++# CONFIG_CRYPTO_LIB_CHACHA is not set ++# CONFIG_CRYPTO_LIB_CURVE25519 is not set ++CONFIG_CRYPTO_LIB_DES=m ++CONFIG_CRYPTO_LIB_POLY1305_RSIZE=9 ++# CONFIG_CRYPTO_LIB_POLY1305 is not set ++# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set ++CONFIG_CRYPTO_LIB_SHA256=y ++CONFIG_CRYPTO_HW=y ++# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set ++# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set ++# CONFIG_CRYPTO_DEV_CCP is not set ++# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set ++# CONFIG_CRYPTO_DEV_CAVIUM_ZIP is not set ++# CONFIG_CRYPTO_DEV_CHELSIO is not set ++CONFIG_CRYPTO_DEV_VIRTIO=m ++# CONFIG_CRYPTO_DEV_SAFEXCEL is not set + CONFIG_CRYPTO_DEV_CCREE=m ++# CONFIG_CRYPTO_DEV_HISI_SEC is not set ++# CONFIG_CRYPTO_DEV_HISI_SEC2 is not set ++# CONFIG_CRYPTO_DEV_HISI_ZIP is not set ++# CONFIG_CRYPTO_DEV_HISI_HPRE is not set ++# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set + CONFIG_TEGRA_CRYPTO_DEV=y + CONFIG_CRYPTO_DEV_TEGRA_SE=y + CONFIG_CRYPTO_DEV_TEGRA_ELLIPTIC_SE=y +@@ -1353,28 +8144,406 @@ CONFIG_CRYPTO_DEV_TEGRA_SE_USE_HOST1X_INTERFACE=y + CONFIG_CRYPTO_DEV_TEGRA_VIRTUAL_SE_INTERFACE=y + CONFIG_CRYPTO_DEV_TEGRA_SE_NVRNG=y + CONFIG_CRYPTO_DEV_TEGRA_NVVSE=y +-CONFIG_CRYPTO_DEV_TEGRA_FDE=m ++CONFIG_ASYMMETRIC_KEY_TYPE=y ++CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y ++CONFIG_X509_CERTIFICATE_PARSER=y ++# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set ++CONFIG_PKCS7_MESSAGE_PARSER=y ++# CONFIG_PKCS7_TEST_KEY is not set ++# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set ++ ++# ++# Certificates for signature checking ++# ++CONFIG_MODULE_SIG_KEY="certs/signing_key.pem" ++CONFIG_SYSTEM_TRUSTED_KEYRING=y ++CONFIG_SYSTEM_TRUSTED_KEYS="" ++# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set ++# CONFIG_SECONDARY_TRUSTED_KEYRING is not set ++# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set ++# end of Certificates for signature checking ++ ++CONFIG_BINARY_PRINTF=y ++ ++# ++# Library routines ++# ++CONFIG_RAID6_PQ=m + # CONFIG_RAID6_PQ_BENCHMARK is not set ++CONFIG_LINEAR_RANGES=y ++CONFIG_PACKING=y ++CONFIG_BITREVERSE=y ++CONFIG_HAVE_ARCH_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_CORDIC=m ++# CONFIG_PRIME_NUMBERS is not set ++CONFIG_RATIONAL=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++CONFIG_ARCH_HAS_FAST_MULTIPLIER=y ++CONFIG_ARCH_USE_SYM_ANNOTATIONS=y ++# CONFIG_INDIRECT_PIO is not set ++CONFIG_CRC_CCITT=y ++CONFIG_CRC16=y ++CONFIG_CRC_T10DIF=y ++CONFIG_CRC_ITU_T=m ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC64 is not set ++# CONFIG_CRC4 is not set ++CONFIG_CRC7=m ++CONFIG_LIBCRC32C=m ++# CONFIG_CRC8 is not set ++CONFIG_XXHASH=y ++CONFIG_AUDIT_GENERIC=y ++CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y ++CONFIG_AUDIT_COMPAT_GENERIC=y ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=m ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_ZSTD_COMPRESS=m ++CONFIG_ZSTD_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_DECOMPRESS_ZSTD=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_REED_SOLOMON=m ++CONFIG_REED_SOLOMON_ENC8=y ++CONFIG_REED_SOLOMON_DEC8=y ++CONFIG_TEXTSEARCH=y ++CONFIG_TEXTSEARCH_KMP=m ++CONFIG_TEXTSEARCH_BM=m ++CONFIG_TEXTSEARCH_FSM=m ++CONFIG_INTERVAL_TREE=y ++CONFIG_XARRAY_MULTI=y ++CONFIG_ASSOCIATIVE_ARRAY=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DMA_OPS=y ++CONFIG_NEED_SG_DMA_LENGTH=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_DMA_ADDR_T_64BIT=y ++CONFIG_DMA_DECLARE_COHERENT=y ++CONFIG_ARCH_HAS_SETUP_DMA_OPS=y ++CONFIG_ARCH_HAS_TEARDOWN_DMA_OPS=y ++CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE=y ++CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU=y ++CONFIG_ARCH_HAS_DMA_PREP_COHERENT=y ++CONFIG_SWIOTLB=y ++CONFIG_DMA_NONCOHERENT_MMAP=y ++CONFIG_DMA_COHERENT_POOL=y ++CONFIG_DMA_REMAP=y ++CONFIG_DMA_DIRECT_REMAP=y + CONFIG_DMA_CMA=y ++# CONFIG_DMA_PERNUMA_CMA is not set ++ ++# ++# Default contiguous memory area size: ++# + CONFIG_CMA_SIZE_MBYTES=64 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set + CONFIG_CMA_ALIGNMENT=9 ++# CONFIG_DMA_API_DEBUG is not set ++CONFIG_SGL_ALLOC=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_GLOB=y ++# CONFIG_GLOB_SELFTEST is not set ++CONFIG_NLATTR=y ++CONFIG_CLZ_TAB=y ++CONFIG_IRQ_POLL=y ++CONFIG_MPILIB=y ++CONFIG_DIMLIB=y ++CONFIG_LIBFDT=y ++CONFIG_OID_REGISTRY=y ++CONFIG_UCS2_STRING=y ++CONFIG_HAVE_GENERIC_VDSO=y ++CONFIG_GENERIC_GETTIMEOFDAY=y ++CONFIG_GENERIC_VDSO_TIME_NS=y ++CONFIG_FONT_SUPPORT=y ++# CONFIG_FONTS is not set ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++CONFIG_SG_POOL=y ++CONFIG_ARCH_STACKWALK=y ++CONFIG_SBITMAP=y ++CONFIG_PARMAN=m ++CONFIG_OBJAGG=m ++# CONFIG_STRING_SELFTEST is not set ++# end of Library routines ++ ++CONFIG_PLDMFW=y ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# + CONFIG_PRINTK_TIME=y ++# CONFIG_PRINTK_CALLER is not set ++CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7 ++CONFIG_CONSOLE_LOGLEVEL_QUIET=4 ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++# CONFIG_DYNAMIC_DEBUG is not set + CONFIG_DYNAMIC_DEBUG_CORE=y + # CONFIG_SYMBOLIC_ERRNAME is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# end of printk and dmesg options ++ ++# ++# Compile-time checks and compiler options ++# + CONFIG_DEBUG_INFO=y ++# CONFIG_DEBUG_INFO_REDUCED is not set ++# CONFIG_DEBUG_INFO_COMPRESSED is not set ++# CONFIG_DEBUG_INFO_SPLIT is not set ++# CONFIG_DEBUG_INFO_DWARF4 is not set ++# CONFIG_DEBUG_INFO_BTF is not set ++# CONFIG_GDB_SCRIPTS is not set ++CONFIG_ENABLE_MUST_CHECK=y + CONFIG_FRAME_WARN=4096 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_HEADERS_INSTALL is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set + # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set ++# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_32B is not set ++CONFIG_ARCH_WANT_FRAME_POINTERS=y ++CONFIG_FRAME_POINTER=y ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# end of Compile-time checks and compiler options ++ ++# ++# Generic Kernel Debugging Instruments ++# + CONFIG_MAGIC_SYSRQ=y ++CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 ++CONFIG_MAGIC_SYSRQ_SERIAL=y ++CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE="" + CONFIG_DEBUG_FS=y ++CONFIG_DEBUG_FS_ALLOW_ALL=y ++# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set ++# CONFIG_DEBUG_FS_ALLOW_NONE is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y ++# CONFIG_UBSAN is not set ++# end of Generic Kernel Debugging Instruments ++ ++CONFIG_DEBUG_KERNEL=y + # CONFIG_DEBUG_MISC is not set ++ ++# ++# Memory Debugging ++# ++# CONFIG_PAGE_EXTENSION is not set ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_PAGE_OWNER is not set ++# CONFIG_PAGE_POISONING is not set ++# CONFIG_DEBUG_PAGE_REF is not set ++# CONFIG_DEBUG_RODATA_TEST is not set ++CONFIG_ARCH_HAS_DEBUG_WX=y ++# CONFIG_DEBUG_WX is not set ++CONFIG_GENERIC_PTDUMP=y ++# CONFIG_PTDUMP_DEBUGFS is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++CONFIG_ARCH_HAS_DEBUG_VM_PGTABLE=y ++# CONFIG_DEBUG_VM is not set ++# CONFIG_DEBUG_VM_PGTABLE is not set ++CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y ++# CONFIG_DEBUG_VIRTUAL is not set ++# CONFIG_DEBUG_MEMORY_INIT is not set ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++CONFIG_HAVE_ARCH_KASAN=y ++CONFIG_HAVE_ARCH_KASAN_SW_TAGS=y ++CONFIG_CC_HAS_KASAN_GENERIC=y ++CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y ++# CONFIG_KASAN is not set ++# end of Memory Debugging ++ ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Oops, Lockups and Hangs ++# + CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_LOCKUP_DETECTOR=y + CONFIG_SOFTLOCKUP_DETECTOR=y + CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y ++CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1 ++CONFIG_DETECT_HUNG_TASK=y ++CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 ++# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set ++CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 + CONFIG_WQ_WATCHDOG=y ++# CONFIG_TEST_LOCKUP is not set ++# end of Debug Oops, Lockups and Hangs ++ ++# ++# Scheduler Debugging ++# ++CONFIG_SCHED_DEBUG=y ++CONFIG_SCHED_INFO=y + CONFIG_SCHEDSTATS=y ++# end of Scheduler Debugging ++ ++# CONFIG_DEBUG_TIMEKEEPING is not set + # CONFIG_DEBUG_PREEMPT is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++CONFIG_LOCK_DEBUGGING_SUPPORT=y ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_RWSEMS is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++# CONFIG_WW_MUTEX_SELFTEST is not set ++# CONFIG_SCF_TORTURE_TEST is not set ++# CONFIG_CSD_LOCK_WAIT_DEBUG is not set ++# end of Lock Debugging (spinlocks, mutexes, etc...) ++ ++CONFIG_STACKTRACE=y ++# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_HAVE_DEBUG_BUGVERBOSE=y ++ ++# ++# Debug kernel data structures ++# ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PLIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_BUG_ON_DATA_CORRUPTION is not set ++# end of Debug kernel data structures ++ ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_RCU_SCALE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_REF_SCALE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=21 ++CONFIG_RCU_TRACE=y ++# CONFIG_RCU_EQS_DEBUG is not set ++# end of RCU Debugging ++ ++# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_NOP_TRACER=y ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACE_CLOCK=y ++CONFIG_RING_BUFFER=y ++CONFIG_EVENT_TRACING=y ++CONFIG_CONTEXT_SWITCH_TRACER=y ++CONFIG_TRACING=y ++CONFIG_GENERIC_TRACER=y ++CONFIG_TRACING_SUPPORT=y ++CONFIG_FTRACE=y ++# CONFIG_BOOTTIME_TRACING is not set ++CONFIG_FUNCTION_TRACER=y ++CONFIG_FUNCTION_GRAPH_TRACER=y + # CONFIG_DYNAMIC_FTRACE is not set ++# CONFIG_FUNCTION_PROFILER is not set + CONFIG_STACK_TRACER=y ++# CONFIG_IRQSOFF_TRACER is not set ++# CONFIG_PREEMPT_TRACER is not set ++# CONFIG_SCHED_TRACER is not set ++# CONFIG_HWLAT_TRACER is not set ++# CONFIG_FTRACE_SYSCALLS is not set ++# CONFIG_TRACER_SNAPSHOT is not set ++CONFIG_BRANCH_PROFILE_NONE=y ++# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set ++# CONFIG_BLK_DEV_IO_TRACE is not set + # CONFIG_UPROBE_EVENTS is not set ++# CONFIG_SYNTH_EVENTS is not set ++# CONFIG_HIST_TRIGGERS is not set ++# CONFIG_TRACE_EVENT_INJECT is not set ++# CONFIG_TRACEPOINT_BENCHMARK is not set ++# CONFIG_RING_BUFFER_BENCHMARK is not set ++# CONFIG_TRACE_EVAL_MAP_FILE is not set ++# CONFIG_FTRACE_STARTUP_TEST is not set ++# CONFIG_RING_BUFFER_STARTUP_TEST is not set ++# CONFIG_PREEMPTIRQ_DELAY_TEST is not set ++# CONFIG_SAMPLES is not set ++CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y ++CONFIG_STRICT_DEVMEM=y ++# CONFIG_IO_STRICT_DEVMEM is not set ++ ++# ++# arm64 Debugging ++# + CONFIG_PID_IN_CONTEXTIDR=y ++# CONFIG_DEBUG_EFI is not set ++# CONFIG_ARM64_RELOC_TEST is not set ++# CONFIG_CORESIGHT is not set ++# end of arm64 Debugging ++ ++# ++# Kernel Testing and Coverage ++# ++# CONFIG_KUNIT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_ARCH_HAS_KCOV=y ++CONFIG_CC_HAS_SANCOV_TRACE_PC=y ++# CONFIG_KCOV is not set + # CONFIG_RUNTIME_TESTING_MENU is not set ++# CONFIG_MEMTEST is not set ++# end of Kernel Testing and Coverage ++# end of Kernel hacking diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0007-gpio-host-gpio-dts.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0007-gpio-host-gpio-dts.patch new file mode 100644 index 000000000..3cf1f0948 --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0007-gpio-host-gpio-dts.patch @@ -0,0 +1,62 @@ +diff --git a/nvidia/soc/t23x/kernel-dts/tegra234-soc/tegra234-soc-base.dtsi b/nvidia/soc/t23x/kernel-dts/tegra234-soc/tegra234-soc-base.dtsi +index 1cf75f2..7dee55c 100644 +--- a/nvidia/soc/t23x/kernel-dts/tegra234-soc/tegra234-soc-base.dtsi ++++ b/nvidia/soc/t23x/kernel-dts/tegra234-soc/tegra234-soc-base.dtsi +@@ -339,6 +339,13 @@ + }; + }; + #endif ++ bpmp_host_proxy: bpmp_host_proxy { ++ compatible = "nvidia,bpmp-host-proxy"; ++ allowed-clocks = ; ++ allowed-resets = ; ++ status = "okay"; ++ }; + + nvrng@3ae0000 { + compatible = "nvidia,tegra234-se-nvrng"; +@@ -444,6 +451,13 @@ + status = "disabled"; + }; + ++ gpio_host_proxy: gpio_host_proxy { ++ compatible = "nvidia,gpio-host-proxy"; ++ // allowed-clocks = <0>; ++ // allowed-resets = <0>; ++ status = "okay"; ++ }; ++ + tegra_gte_lic: gte@3aa0000 { + compatible = "nvidia,tegra194-gte-lic"; + reg = <0x0 0x3aa0000 0x0 0x10000>; +diff --git a/nvidia/soc/t23x/kernel-dts/tegra234-soc/tegra234-soc-uart.dtsi b/nvidia/soc/t23x/kernel-dts/tegra234-soc/tegra234-soc-uart.dtsi +index 503cb27..8a74263 100644 +--- a/nvidia/soc/t23x/kernel-dts/tegra234-soc/tegra234-soc-uart.dtsi ++++ b/nvidia/soc/t23x/kernel-dts/tegra234-soc/tegra234-soc-uart.dtsi +@@ -28,10 +28,10 @@ + serial5 = &uartf; + serial7 = &uarth; + }; +- +- uarta: serial@3100000 { +- compatible = "nvidia,tegra194-hsuart"; +- iommus = <&smmu_niso0 TEGRA_SID_NISO0_GPCDMA_0>; ++ uarta: serial@3100000 { ++ compatible = "nvidia,tegra194-dummy", "nvidia,vfio-platform"; ++ //iommus = <&smmu_niso0 TEGRA_SID_NISO0_GPCDMA_0>; ++ iommus = <&smmu_niso0 TEGRA_SID_NISO1_SMMU_TEST>; + dma-coherent; + reg = <0x0 0x03100000 0x0 0x10000>; + reg-shift = <2>; +@@ -44,8 +44,8 @@ + clock-names = "serial", "parent"; + resets = <&bpmp_resets TEGRA234_RESET_UARTA>; + reset-names = "serial"; +- status = "disabled"; +- }; ++ status = "okay"; ++ }; + + uartb: serial@3110000 { + compatible = "nvidia,tegra194-hsuart"; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/compile.log b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/compile.log new file mode 100644 index 000000000..e69de29bb diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/dummy.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/dummy.patch new file mode 100644 index 000000000..e69de29bb diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/default.nix index acf8d716f..d4620adbb 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/default.nix @@ -2,6 +2,8 @@ # SPDX-License-Identifier: Apache-2.0 { imports = [ + ./common/gpio-virt-common + ./host/gpio-virt-host ./common/bpmp-virt-common ./host/bpmp-virt-host ./host/uarta-host diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix new file mode 100644 index 000000000..2c64b5841 --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix @@ -0,0 +1,75 @@ +# Copyright 2022-2023 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + pkgs, + config, + ... +}: let + cfg = config.ghaf.hardware.nvidia.virtualization.host.gpio; +in { + options.ghaf.hardware.nvidia.virtualization.host.gpio.enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Enable virtualization host support for NVIDIA Orin + + This option is an implementation level detail and is toggled automatically + by modules that need it. Manually enabling this option is not recommended in + release builds. + ''; + }; + + config = lib.mkIf cfg.enable { + nixpkgs.overlays = [(import ./overlays/qemu)]; + + ghaf.hardware.nvidia.virtualization.enable = true; + + boot.kernelPatches = [ + /* we use overlays not patches for DT + { + name = "Gpio virtualisation host proxy device tree" + patch = ./patches/0007-gpio-host-gpio-dts.patch + } + { + name = "Gpio virtualization host uarta device tree"; + patch = ./patches/0002-gpio-host-uarta-dts.patch; + } + */ + { + name = "GPIO virtualization host kernel configuration"; + patch = null; + extraStructuredConfig = with lib.kernel; { + VFIO_PLATFORM = yes; + TEGRA_GPIO_HOST_PROXY = yes; + }; + } + ]; + + # this differes from bpmp implementation in that DT is overlayed here + hardware.deviceTree = { + # Enable hardware.deviceTree for handle host dtb overlays + enable = true; + # name = "tegra234-p3701-0000-p3737-0000.dtb"; + name = "tegra234-p3701-host-passthrough.dtb"; + + # using overlay file: + overlays = [ + { + name = "gpio_pt_host_overlay"; + dtsFile = ./gpio_pt_host_overlay.dtso; + + # Apply overlay only to host passthrough device tree + # filter = "tegra234-p3701-0000-p3737-0000.dtb"; + # filter = "tegra234-p3701-host-passthrough.dtb"; + } + ]; + }; + + # TODO: Consider if these are really needed, maybe add only in debug builds? + environment.systemPackages = with pkgs; [ + qemu + dtc + ]; + }; +} diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/gpio_pt_host_overlay.dtso b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/gpio_pt_host_overlay.dtso new file mode 100644 index 000000000..47bbb567e --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/gpio_pt_host_overlay.dtso @@ -0,0 +1,17 @@ +/dts-v1/; +/plugin/; + +/{ + overlay-name = "GPIO passthrough on host"; + compatible = "nvidia,p3737-0000+p3701-0000\0nvidia,tegra234\0nvidia,tegra23x"; + + fragment@0 + { target-path = "/"; + __overlay__ + { gpio_host_proxy + { compatible = "nvidia,gpio-host-proxy"; + status = "okay"; + }; + }; + }; +}; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/overlays/qemu/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/overlays/qemu/default.nix new file mode 100644 index 000000000..eb4037d74 --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/overlays/qemu/default.nix @@ -0,0 +1,11 @@ +# Copyright 2022-2023 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +(_final: prev: { + qemu = prev.qemu.overrideAttrs (_final: prev: { + patches = + prev.patches + ++ [ + ./patches/0001-qemu-v8.1.3_gpio-virt.patch + ]; + }); +}) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/overlays/qemu/patches/0001-qemu-v8.1.3_gpio-virt.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/overlays/qemu/patches/0001-qemu-v8.1.3_gpio-virt.patch new file mode 100644 index 000000000..356e8c01a --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/overlays/qemu/patches/0001-qemu-v8.1.3_gpio-virt.patch @@ -0,0 +1,357 @@ +From e7d0fd8ca9f4334ee5d980217044e9c1c7cc21f4 Mon Sep 17 00:00:00 2001 +From: Mika Tammi +Date: Sun, 7 Jan 2024 00:24:33 +0200 +Subject: Rebased patch on top of QEMU 8.1.3 + +--- + README.md | 50 +++++++++++ + configure.sh | 1 + + hw/arm/Kconfig | 1 + + hw/arm/virt.c | 5 ++ + hw/misc/Kconfig | 3 + + hw/misc/meson.build | 1 + + hw/misc/nvidia_bpmp_guest.c | 171 ++++++++++++++++++++++++++++++++++++ + hw/misc/nvidia_bpmp_guest.h | 9 ++ + include/hw/arm/virt.h | 1 + + 9 files changed, 242 insertions(+) + create mode 100644 README.md + create mode 100755 configure.sh + create mode 100644 hw/misc/nvidia_bpmp_guest.c + create mode 100644 hw/misc/nvidia_bpmp_guest.h + +diff --git a/README.md b/README.md +new file mode 100644 +index 0000000000..56b9e765ec +--- /dev/null ++++ b/README.md +@@ -0,0 +1,50 @@ ++ ++# Short intstructions: ++ ++ ++1. git clone https://gitlab.com/qemu-project/qemu.git ++2 cd qemu ++3 git checkout -b v8.1.3 ++4 ./configure --target-list=aarm64-softmmu ++5 make -j12 ++ ++ ++ ++Device memory map: ++ ++0x090c0000 + /* Base address, size 0x01000 */ ++ ++ 0x0000 \ Tx buffer ++ 0x01FF / ++ 0x0200 \ Rx buffer ++ 0x03FF / ++ 0x0400 -- Tx size ++ 0x0408 -- Rx size ++ 0x0410 -- Ret ++ 0x0500 -- mrq ++ ++ ++ ++ Data should be aligned to 64bit paragraph. ++ ++ Protocol is: ++ 1. Write data buffers to 0x0000-0x01FF and 0x0200-0x03FF ++ 2. Write buffer sizes to 0x0400 (Tx) and 0x0408 (Rx) ++ 2. Start operation by writing mrq opcode to address 0x0500 ++ 3. Read ret code from 0x0410 and response data from the buffers ++ ++ ++For reading and writing busybox may be used as: ++ ++ busybox devmem 0x090c0000 ++ ++and so on ++ ++For instance, to reset the UARTA (with ID 0x64) you can sed the next ++command: ++ ++ busybox devmem 0x090c0000 64 0x0000006400000001 ++ busybox devmem 0x090c0400 8 0x08 ++ busybox devmem 0x090c0500 8 0x14 ++ ++``` +diff --git a/configure.sh b/configure.sh +new file mode 100755 +index 0000000000..cb82ca69a7 +--- /dev/null ++++ b/configure.sh +@@ -0,0 +1 @@ ++./configure --target-list=aarch64-softmmu --enable-sdl --enable-gtk -enable-vhost-kernel --enable-vhost-net --enable-vhost-user --enable-vhost-user-blk-server --enable-vfio-user-server --enable-vhost-crypto --enable-vhost-vdpa --enable-virglrenderer --enable-virtfs --enable-virtfs-proxy-helper +diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig +index 7e68348440..3653c77f9e 100644 +--- a/hw/arm/Kconfig ++++ b/hw/arm/Kconfig +@@ -32,6 +32,7 @@ config ARM_VIRT + select VIRTIO_MEM_SUPPORTED + select ACPI_CXL + select ACPI_HMAT ++ select NVIDIA_BPMP_GUEST + + config CHEETAH + bool +diff --git a/hw/arm/virt.c b/hw/arm/virt.c +index 7d9dbc2663..5ee4ec077a 100644 +--- a/hw/arm/virt.c ++++ b/hw/arm/virt.c +@@ -79,6 +79,7 @@ + #include "hw/virtio/virtio-md-pci.h" + #include "hw/virtio/virtio-iommu.h" + #include "hw/char/pl011.h" ++#include "hw/misc/nvidia_bpmp_guest.h" + #include "qemu/guest-random.h" + + #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ +@@ -155,6 +156,7 @@ static const MemMapEntry base_memmap[] = { + [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, + [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, + [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, ++ [VIRT_NVIDIA_BPMP_GUEST] = { 0x090c0000, 0x00001000 }, + [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, + /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ + [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, +@@ -1046,6 +1048,9 @@ static void create_virtio_devices(const VirtMachineState *vms) + hwaddr size = vms->memmap[VIRT_MMIO].size; + MachineState *ms = MACHINE(vms); + ++ /* Create NVIDIA BPMP guest passthru device, possibly need update ftd - WIP */ ++ nvidia_bpmp_guest_create(vms->memmap[VIRT_NVIDIA_BPMP_GUEST].base); ++ + /* We create the transports in forwards order. Since qbus_realize() + * prepends (not appends) new child buses, the incrementing loop below will + * create a list of virtio-mmio buses with decreasing base addresses. +diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig +index 6996d265e4..30b4c34e69 100644 +--- a/hw/misc/Kconfig ++++ b/hw/misc/Kconfig +@@ -11,6 +11,9 @@ config ARMSSE_MHU + config ARMSSE_CPU_PWRCTRL + bool + ++config NVIDIA_BPMP_GUEST ++ bool ++ + config ISA_DEBUG + bool + depends on ISA_BUS +diff --git a/hw/misc/meson.build b/hw/misc/meson.build +index 892f8b91c5..f623cf2c9c 100644 +--- a/hw/misc/meson.build ++++ b/hw/misc/meson.build +@@ -1,4 +1,5 @@ + system_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c')) ++system_ss.add(when: 'CONFIG_NVIDIA_BPMP_GUEST', if_true: files('nvidia_bpmp_guest.c')) + system_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) + system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) + system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) +diff --git a/hw/misc/nvidia_bpmp_guest.c b/hw/misc/nvidia_bpmp_guest.c +new file mode 100644 +index 0000000000..3facee6d00 +--- /dev/null ++++ b/hw/misc/nvidia_bpmp_guest.c +@@ -0,0 +1,171 @@ ++#include "qemu/osdep.h" ++#include "qemu/log.h" ++#include "qapi/error.h" /* provides error_fatal() handler */ ++#include "hw/sysbus.h" /* provides all sysbus registering func */ ++#include "hw/misc/nvidia_bpmp_guest.h" ++ ++#define TYPE_NVIDIA_BPMP_GUEST "nvidia_bpmp_guest" ++typedef struct NvidiaBpmpGuestState NvidiaBpmpGuestState; ++DECLARE_INSTANCE_CHECKER(NvidiaBpmpGuestState, NVIDIA_BPMP_GUEST, TYPE_NVIDIA_BPMP_GUEST) ++ ++#define TX_BUF 0x0000 ++#define RX_BUF 0x0200 ++#define TX_SIZ 0x0400 ++#define RX_SIZ 0x0408 ++#define RET_COD 0x0410 ++#define MRQ 0x0500 ++ ++#define MEM_SIZE 0x600 ++#define HOST_DEVICE_PATH "/dev/bpmp-host" ++#define MESSAGE_SIZE 0x0200 ++ ++// qemu_log_mask(LOG_UNIMP, "%s: \n", __func__ ); ++ ++struct NvidiaBpmpGuestState ++{ ++ SysBusDevice parent_obj; ++ MemoryRegion iomem; ++ int host_device_fd; ++ uint8_t mem[MEM_SIZE]; ++}; ++ ++// Device memory map: ++ ++// 0x090c0000 + /* Base address, size 0x01000 */ ++ ++// 0x0000 \ Tx buffer ++// 0x01FF / ++// 0x0200 \ Rx buffer ++// 0x03FF / ++// 0x0400 -- Tx size ++// 0x0408 -- Rx size ++// 0x0410 -- Ret ++// 0x0500 -- mrq ++ ++ ++ ++// Data should be aligned to 64bit paragraph. ++ ++// Protocol is: ++// 1. Write data buffers to 0x0000-0x01FF and 0x0200-0x03FF ++// 2. Write buffer sizes to 0x0400 (Tx) and 0x0408 (Rx) ++// 2. Start operation by writing mrq opcode to address 0x0500 ++// 3. Read ret code from 0x0410 and response data from the buffers ++ ++static uint64_t nvidia_bpmp_guest_read(void *opaque, hwaddr addr, unsigned int size) ++{ ++ NvidiaBpmpGuestState *s = opaque; ++ ++ if (addr >= MEM_SIZE) ++ return 0xDEADBEEF; ++ ++ // Cast buffer location as uint64_t ++ return *(uint64_t*)&s->mem[addr]; ++} ++ ++static void nvidia_bpmp_guest_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) ++{ ++ NvidiaBpmpGuestState *s = opaque; ++ int ret; ++ ++ struct ++ { ++ unsigned int mrq; ++ struct ++ { ++ void *data; ++ size_t size; ++ } tx; ++ struct ++ { ++ void *data; ++ size_t size; ++ int ret; ++ } rx; ++ } messg; ++ ++ memset(&messg, 0, sizeof(messg)); ++ ++ if (addr >= MEM_SIZE){ ++ qemu_log_mask(LOG_UNIMP, "qemu: Error addr >= MEM_SIZE in 0x%lX data: 0x%lX\n", addr, data); ++ return; ++ } ++ ++ switch (addr) ++ { ++ case MRQ: ++ // set up the structure ++ messg.mrq = data; ++ messg.tx.data = &s->mem[TX_BUF]; ++ memcpy(&messg.tx.size, &s->mem[TX_SIZ], sizeof(messg.tx.size)); ++ messg.rx.data = &s->mem[RX_BUF]; ++ memcpy(&messg.rx.size, &s->mem[RX_SIZ], sizeof(messg.rx.size)); ++ ++ ret = write(s->host_device_fd, &messg, sizeof(messg)); // Send the data to the host module ++ if (ret < 0) ++ { ++ qemu_log_mask(LOG_UNIMP, "%s: Failed to write the host device..\n", __func__); ++ return; ++ } ++ ++ memcpy(&s->mem[RET_COD], &messg.rx.ret, sizeof(messg.rx.ret)); ++ memcpy(&s->mem[RX_SIZ], &messg.rx.size, sizeof(messg.rx.size)); ++ ++ break; ++ ++ default: ++ ++ memcpy(&s->mem[addr], &data, size); ++ } ++ ++ return; ++} ++ ++static const MemoryRegionOps nvidia_bpmp_guest_ops = { ++ .read = nvidia_bpmp_guest_read, ++ .write = nvidia_bpmp_guest_write, ++ .endianness = DEVICE_NATIVE_ENDIAN, ++}; ++ ++static void nvidia_bpmp_guest_instance_init(Object *obj) ++{ ++ NvidiaBpmpGuestState *s = NVIDIA_BPMP_GUEST(obj); ++ ++ /* allocate memory map region */ ++ memory_region_init_io(&s->iomem, obj, &nvidia_bpmp_guest_ops, s, TYPE_NVIDIA_BPMP_GUEST, MEM_SIZE); ++ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); ++ ++ s->host_device_fd = open(HOST_DEVICE_PATH, O_RDWR); // Open the device with read/write access ++ ++ if (s->host_device_fd < 0) ++ { ++ qemu_log_mask(LOG_UNIMP, "%s: Failed to open the host device..\n", __func__); ++ return; ++ } ++} ++ ++/* create a new type to define the info related to our device */ ++static const TypeInfo nvidia_bpmp_guest_info = { ++ .name = TYPE_NVIDIA_BPMP_GUEST, ++ .parent = TYPE_SYS_BUS_DEVICE, ++ .instance_size = sizeof(NvidiaBpmpGuestState), ++ .instance_init = nvidia_bpmp_guest_instance_init, ++}; ++ ++static void nvidia_bpmp_guest_register_types(void) ++{ ++ type_register_static(&nvidia_bpmp_guest_info); ++} ++ ++type_init(nvidia_bpmp_guest_register_types) ++ ++ /* ++ * Create the Nvidia BPMP guest device. ++ */ ++ DeviceState *nvidia_bpmp_guest_create(hwaddr addr) ++{ ++ DeviceState *dev = qdev_new(TYPE_NVIDIA_BPMP_GUEST); ++ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); ++ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); ++ return dev; ++} +diff --git a/hw/misc/nvidia_bpmp_guest.h b/hw/misc/nvidia_bpmp_guest.h +new file mode 100644 +index 0000000000..dd4b4221d3 +--- /dev/null ++++ b/hw/misc/nvidia_bpmp_guest.h +@@ -0,0 +1,9 @@ ++#ifndef HW_NVIDIA_BPMP_GUEST_H ++#define HW_NVIDIA_BPMP_GUEST_H ++ ++#include "qom/object.h" ++ ++DeviceState *nvidia_bpmp_guest_create(hwaddr); ++ ++ ++#endif +diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h +index e1ddbea96b..fce00ae7e9 100644 +--- a/include/hw/arm/virt.h ++++ b/include/hw/arm/virt.h +@@ -77,6 +77,7 @@ enum { + VIRT_PCIE_MMIO, + VIRT_PCIE_PIO, + VIRT_PCIE_ECAM, ++ VIRT_NVIDIA_BPMP_GUEST, + VIRT_PLATFORM_BUS, + VIRT_GPIO, + VIRT_SECURE_UART, +-- +2.42.0 + diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/patches/0002-gpio-host-uarta-dts.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/patches/0002-gpio-host-uarta-dts.patch new file mode 100644 index 000000000..2be2c43be --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/patches/0002-gpio-host-uarta-dts.patch @@ -0,0 +1,46 @@ +diff --git a/nvidia/soc/t23x/kernel-dts/tegra234-soc/tegra234-soc-uart.dtsi b/nvidia/soc/t23x/kernel-dts/tegra234-soc/tegra234-soc-uart.dtsi +index 503cb275dcc1..c708ad3f5048 100644 +--- a/nvidia/soc/t23x/kernel-dts/tegra234-soc/tegra234-soc-uart.dtsi ++++ b/nvidia/soc/t23x/kernel-dts/tegra234-soc/tegra234-soc-uart.dtsi +@@ -29,23 +29,24 @@ aliases { + serial7 = &uarth; + }; + +- uarta: serial@3100000 { +- compatible = "nvidia,tegra194-hsuart"; +- iommus = <&smmu_niso0 TEGRA_SID_NISO0_GPCDMA_0>; +- dma-coherent; +- reg = <0x0 0x03100000 0x0 0x10000>; +- reg-shift = <2>; +- interrupts = <0 TEGRA234_IRQ_UARTA 0x04>; +- nvidia,memory-clients = <14>; +- dmas = <&gpcdma 8>, <&gpcdma 8>; +- dma-names = "rx", "tx"; +- clocks = <&bpmp_clks TEGRA234_CLK_UARTA>, +- <&bpmp_clks TEGRA234_CLK_PLLP_OUT0>; +- clock-names = "serial", "parent"; +- resets = <&bpmp_resets TEGRA234_RESET_UARTA>; +- reset-names = "serial"; +- status = "disabled"; +- }; ++ uarta: serial@3100000 { ++ compatible = "nvidia,tegra194-dummy"; ++ //iommus = <&smmu_niso0 TEGRA_SID_NISO0_GPCDMA_0>; ++ iommus = <&smmu_niso0 TEGRA_SID_NISO1_SMMU_TEST>; ++ dma-coherent; ++ reg = <0x0 0x03100000 0x0 0x10000>; ++ reg-shift = <2>; ++ interrupts = <0 TEGRA234_IRQ_UARTA 0x04>; ++ nvidia,memory-clients = <14>; ++ dmas = <&gpcdma 8>, <&gpcdma 8>; ++ dma-names = "rx", "tx"; ++ clocks = <&bpmp_clks TEGRA234_CLK_UARTA>, ++ <&bpmp_clks TEGRA234_CLK_PLLP_OUT0>; ++ clock-names = "serial", "parent"; ++ resets = <&bpmp_resets TEGRA234_RESET_UARTA>; ++ reset-names = "serial"; ++ status = "okay"; ++ }; + + uartb: serial@3110000 { + compatible = "nvidia,tegra194-hsuart"; diff --git a/modules/microvm/default.nix b/modules/microvm/default.nix index d9c58bcfb..933958140 100644 --- a/modules/microvm/default.nix +++ b/modules/microvm/default.nix @@ -9,6 +9,7 @@ ./virtualization/microvm/netvm.nix ./virtualization/microvm/appvm.nix ./virtualization/microvm/guivm.nix + ./virtualization/microvm/gpiovm.nix ./networking.nix ]; } diff --git a/modules/microvm/virtualization/microvm/gpiovm.nix b/modules/microvm/virtualization/microvm/gpiovm.nix new file mode 100644 index 000000000..912c9acba --- /dev/null +++ b/modules/microvm/virtualization/microvm/gpiovm.nix @@ -0,0 +1,134 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: let + configHost = config; + vmName = "gpio-vm"; + macAddress = "02:00:00:02:02:02"; + netvmBaseConfiguration = { + imports = [ + (import ./common/vm-networking.nix {inherit vmName macAddress;}) + ({lib, ...}: { + ghaf = { + users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; + development = { + # NOTE: SSH port also becomes accessible on the network interface + # that has been passed through to NetVM + ssh.daemon.enable = lib.mkDefault configHost.ghaf.development.ssh.daemon.enable; + debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; + nix-setup.enable = lib.mkDefault configHost.ghaf.development.nix-setup.enable; + }; + systemd = { + enable = true; + withName = "gpiovm-systemd"; + withPolkit = true; + withDebug = configHost.ghaf.profiles.debug.enable; + }; + }; + + system.stateVersion = lib.trivial.release; + + nixpkgs.buildPlatform.system = configHost.nixpkgs.buildPlatform.system; + nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system; + + microvm.hypervisor = "qemu"; + + networking = { + firewall.allowedTCPPorts = [53]; + firewall.allowedUDPPorts = [53]; + }; + + # Add simple wi-fi connection helper + environment.systemPackages = lib.mkIf config.ghaf.profiles.debug.enable [pkgs.wifi-connector]; + + # dnsmask is probably not necessary for gpio VM's networking needs + # note we switch from NetVm's 192.168.100.0/24 base to 192.168.103.0/24 + # Dnsmasq is used as a DHCP/DNS server inside the VM + services.dnsmasq = { + enable = true; + resolveLocalQueries = true; + settings = { + server = ["8.8.8.8"]; + dhcp-range = ["192.168.103.2,192.168.103.254"]; + dhcp-sequential-ip = true; + dhcp-authoritative = true; + domain = "ghaf"; + listen-address = ["127.0.0.1,192.168.103.1"]; + dhcp-option = [ + "option:router,192.168.103.1" + "6,192.168.103.1" + ]; + expand-hosts = true; + domain-needed = true; + bogus-priv = true; + }; + }; + + # Disable resolved since we are using Dnsmasq + services.resolved.enable = false; + + systemd.network = { + enable = true; + networks."10-ethint0" = { + matchConfig.MACAddress = macAddress; + addresses = [ + { + addressConfig.Address = "192.168.103.3/24"; + } + { +# note: same debugging subnet as for NetVm + # IP-address for debugging subnet + addressConfig.Address = "192.168.101.3/24"; + } + ]; + linkConfig.ActivationPolicy = "always-up"; + }; + }; + + microvm = { + optimize.enable = true; + shares = [ + { + tag = "ro-store"; + source = "/nix/store"; + mountPoint = "/nix/.ro-store"; + } + ]; + writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; + }; + + imports = [../../../common]; + }) + ]; + }; + cfg = config.ghaf.virtualization.microvm.gpiovm; +in { + options.ghaf.virtualization.microvm.gpiovm = { + enable = lib.mkEnableOption "GpioVM"; + + extraModules = lib.mkOption { + description = '' + List of additional modules to be imported and evaluated as part of + GpioVM's NixOS configuration. + ''; + default = []; + }; + }; + + config = lib.mkIf cfg.enable { + microvm.vms."${vmName}" = { + autostart = true; + config = + netvmBaseConfiguration + // { + imports = + netvmBaseConfiguration.imports + ++ cfg.extraModules; + }; + }; + }; +} diff --git a/targets/nvidia-jetson-orin/flake-module.nix b/targets/nvidia-jetson-orin/flake-module.nix index 4b9d00e84..9458cd903 100644 --- a/targets/nvidia-jetson-orin/flake-module.nix +++ b/targets/nvidia-jetson-orin/flake-module.nix @@ -59,6 +59,7 @@ hardware.nvidia = { virtualization.enable = false; + virtualization.host.gpio.enable = true; # set true temporarily for debug virtualization.host.bpmp.enable = false; passthroughs.host.uarta.enable = false; }; From 288cb92d3e60884c3c270c58a58d998d2fa1ef40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Wed, 3 Apr 2024 13:32:12 +0300 Subject: [PATCH 02/26] bugfix --- hydrajobs/flake-module.nix | 1 - .../virtualization/common/gpio-virt-common/default.nix | 7 +++---- .../virtualization/host/gpio-virt-host/default.nix | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/hydrajobs/flake-module.nix b/hydrajobs/flake-module.nix index 30d763e7c..3d76a62ae 100644 --- a/hydrajobs/flake-module.nix +++ b/hydrajobs/flake-module.nix @@ -5,7 +5,6 @@ bpmpEnableModule = {lib, ...}: { ghaf.hardware.nvidia = { virtualization.enable = lib.mkForce true; - virtualization.host.gpio.enable = lib.mkForce true; virtualization.host.bpmp.enable = lib.mkForce true; passthroughs.host.uarta.enable = lib.mkForce true; }; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix index cb40f6e35..f8a5df489 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix @@ -7,6 +7,7 @@ }: let cfg = config.ghaf.hardware.nvidia.virtualization; in { + /* alaso defined in bpmp-virt-common. It's top level, should be moved from there options.ghaf.hardware.nvidia.virtualization.enable = lib.mkOption { type = lib.types.bool; default = false; @@ -18,6 +19,7 @@ in { release builds. ''; }; + */ config = lib.mkIf cfg.enable { boot.kernelPatches = [ @@ -26,9 +28,6 @@ in { patch = null; extraStructuredConfig = with lib.kernel; { PCI_STUB = lib.mkDefault yes; - VFIO = lib.mkDefault yes; - VIRTIO_PCI = lib.mkDefault yes; - VIRTIO_MMIO = lib.mkDefault yes; HOTPLUG_PCI = lib.mkDefault yes; PCI_DEBUG = lib.mkDefault yes; PCI_HOST_GENERIC = lib.mkDefault yes; @@ -36,9 +35,9 @@ in { HOTPLUG_PCI_ACPI = lib.mkDefault yes; PCI_HOST_COMMON = lib.mkDefault yes; VFIO_PLATFORM = lib.mkDefault yes; + VFIO = lib.mkDefault yes; VIRTIO_PCI = lib.mkDefault yes; VIRTIO_MMIO = lib.mkDefault yes; - VFIO = lib.mkDefault yes; CONFIG_GPIO_TEGRA = lib.mkDefault yes; CONFIG_GPIO_TEGRA186 = lib.mkDefault yes; TEGRA_GPIO_GUEST_PROXY = lib.mkDefault no; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix index 2c64b5841..be0e56e6b 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix @@ -23,7 +23,7 @@ in { config = lib.mkIf cfg.enable { nixpkgs.overlays = [(import ./overlays/qemu)]; - ghaf.hardware.nvidia.virtualization.enable = true; + # ghaf.hardware.nvidia.virtualization.enable = true; boot.kernelPatches = [ /* we use overlays not patches for DT From 2ff3c027d0a0222355d049309e025a044eacfc4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Wed, 3 Apr 2024 14:50:59 +0300 Subject: [PATCH 03/26] still compile problems after merge with newest tiiuae/ghaf --- .../common/bpmp-virt-common/default.nix | 12 ----- .../common/gpio-virt-common/default.nix | 31 ++++-------- .../virtualization/default.nix | 48 +++++++++++++++++++ targets/nvidia-jetson-orin/flake-module.nix | 8 +++- 4 files changed, 63 insertions(+), 36 deletions(-) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix index c2b5968a2..2ed539f14 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix @@ -7,18 +7,6 @@ }: let cfg = config.ghaf.hardware.nvidia.virtualization; in { - options.ghaf.hardware.nvidia.virtualization.enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - Enable virtualization support for NVIDIA Orin - - This option is an implementation level detail and is toggled automatically - by modules that need it. Manually enabling this option is not recommended in - release builds. - ''; - }; - config = lib.mkIf cfg.enable { boot.kernelPatches = [ { diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix index f8a5df489..905df9369 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix @@ -7,20 +7,6 @@ }: let cfg = config.ghaf.hardware.nvidia.virtualization; in { - /* alaso defined in bpmp-virt-common. It's top level, should be moved from there - options.ghaf.hardware.nvidia.virtualization.enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - Enable virtualization support for NVIDIA Orin - - This option is an implementation level detail and is toggled automatically - by modules that need it. Manually enabling this option is not recommended in - release builds. - ''; - }; - */ - config = lib.mkIf cfg.enable { boot.kernelPatches = [ { @@ -29,26 +15,28 @@ in { extraStructuredConfig = with lib.kernel; { PCI_STUB = lib.mkDefault yes; HOTPLUG_PCI = lib.mkDefault yes; + HOTPLUG_PCI_ACPI = lib.mkDefault yes; PCI_DEBUG = lib.mkDefault yes; PCI_HOST_GENERIC = lib.mkDefault yes; - VFIO_IOMMU_TYPE1 = lib.mkDefault yes; - HOTPLUG_PCI_ACPI = lib.mkDefault yes; PCI_HOST_COMMON = lib.mkDefault yes; - VFIO_PLATFORM = lib.mkDefault yes; VFIO = lib.mkDefault yes; + VFIO_IOMMU_TYPE1 = lib.mkDefault yes; + VFIO_PLATFORM = lib.mkDefault yes; VIRTIO_PCI = lib.mkDefault yes; VIRTIO_MMIO = lib.mkDefault yes; CONFIG_GPIO_TEGRA = lib.mkDefault yes; CONFIG_GPIO_TEGRA186 = lib.mkDefault yes; - TEGRA_GPIO_GUEST_PROXY = lib.mkDefault no; - TEGRA_GPIO_HOST_PROXY = lib.mkDefault no; + TEGRA_GPIO_GUEST_PROXY = lib.mkDefault yes; + TEGRA_GPIO_HOST_PROXY = lib.mkDefault yes; }; } - + + /* not necessary handled with boot parameters { name = "Vfio_platform Reset Required False"; patch = ./patches/0002-vfio_platform-reset-required-false.patch; } + */ { name = "GPIO Support Virtualization"; patch = ./patches/0003-gpio-virt-kernel.patch; @@ -63,12 +51,11 @@ in { patch = ./patches/0005-gpio-overlay.patch; } */ - /* kerenel configuration is direct not with a defconfig patch + # for now kerenel configuration is not direct -- a defconfig patchfile does it { name = "GPIO defconfig"; patch = ./patches/0006-defconfig-kernel.patch; } - */ /* removed, because we use overelay files { name = "GPIO dtsfiles"; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/default.nix index d4620adbb..f306602fd 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/default.nix @@ -1,6 +1,54 @@ # Copyright 2022-2023 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 { + lib, + config, + ... +}: let + cfg = config.ghaf.hardware.nvidia.virtualization; +in { + options.ghaf.hardware.nvidia.virtualization.enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Enable virtualization support for NVIDIA Orin + + This option is an implementation level detail and is toggled automatically + by modules that need it. Manually enabling this option is not recommended in + release builds. + ''; + }; + + /* + config = lib.mkIf cfg.enable { + boot.kernelPatches = [ + { + name = "Added Configurations to Support Vda"; + patch = null; + extraStructuredConfig = with lib.kernel; { + PCI_STUB = lib.mkDefault yes; + VFIO = lib.mkDefault yes; + VIRTIO_PCI = lib.mkDefault yes; + VIRTIO_MMIO = lib.mkDefault yes; + HOTPLUG_PCI = lib.mkDefault yes; + PCI_DEBUG = lib.mkDefault yes; + PCI_HOST_GENERIC = lib.mkDefault yes; + VFIO_IOMMU_TYPE1 = lib.mkDefault yes; + HOTPLUG_PCI_ACPI = lib.mkDefault yes; + PCI_HOST_COMMON = lib.mkDefault yes; + VFIO_PLATFORM = lib.mkDefault yes; + TEGRA_BPMP_GUEST_PROXY = lib.mkDefault no; + TEGRA_BPMP_HOST_PROXY = lib.mkDefault no; + TEGRA_GPIO_GUEST_PROXY = lib.mkDefault no; + TEGRA_GPIO_HOST_PROXY = lib.mkDefault no; + }; + } + ]; + + boot.kernelParams = ["vfio_iommu_type1.allow_unsafe_interrupts=1"]; + }; + */ + imports = [ ./common/gpio-virt-common ./host/gpio-virt-host diff --git a/targets/nvidia-jetson-orin/flake-module.nix b/targets/nvidia-jetson-orin/flake-module.nix index 9458cd903..94abdd96d 100644 --- a/targets/nvidia-jetson-orin/flake-module.nix +++ b/targets/nvidia-jetson-orin/flake-module.nix @@ -53,8 +53,12 @@ hardware.nvidia.orin = { enable = true; somType = som; - agx.enableNetvmWlanPCIPassthrough = som == "agx"; - nx.enableNetvmEthernetPCIPassthrough = som == "nx"; + #agx.enableNetvmWlanPCIPassthrough = som == "agx"; + #nx.enableNetvmEthernetPCIPassthrough = som == "nx"; + + # removed for debugging + agx.enableNetvmWlanPCIPassthrough = false; + nx.enableNetvmEthernetPCIPassthrough = false; }; hardware.nvidia = { From 71cd4dc0c82cc5c96343624a0fc5926887c808e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Wed, 10 Apr 2024 12:36:05 +0300 Subject: [PATCH 04/26] add missing copyright statements --- modules/microvm/virtualization/microvm/gpio-test.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/microvm/virtualization/microvm/gpio-test.nix b/modules/microvm/virtualization/microvm/gpio-test.nix index 5093b53e7..23f125026 100644 --- a/modules/microvm/virtualization/microvm/gpio-test.nix +++ b/modules/microvm/virtualization/microvm/gpio-test.nix @@ -1,3 +1,5 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 { pkgs ? import {} }: let scriptPath = pkgs.concatTextFile { From 39803c486638dc4ed41f5ab028608334daa1a106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Wed, 10 Apr 2024 15:56:17 +0000 Subject: [PATCH 05/26] Refactoring of code because of merge with newest tiiuae Ghaf - changes concern activation and deactivation of virtualised elements in passthough of uarta, bpmp, gpio. - code deactivates uarta and bpmp because bpmp kernel patches conflict with gpio patches. This could be fixed by remaking the bpmp patches. --- build.sh | 8 +- .../common/bpmp-virt-common/default.nix | 3 +- .../common/gpio-virt-common/default.nix | 11 +- .../virtualization/overlays/default.nix | 11 + .../virtualization/overlays/qemu/default.nix | 11 + .../patches/0001-qemu-v8.1.3_bpmp-virt.patch | 357 ++++++++++++++++++ .../patches/0001-qemu-v8.1.3_gpio-virt.patch | 357 ++++++++++++++++++ .../microvm/virtualization/microvm/gpiovm.nix | 21 +- targets/nvidia-jetson-orin/flake-module.nix | 19 +- 9 files changed, 779 insertions(+), 19 deletions(-) create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/overlays/default.nix create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/overlays/qemu/default.nix create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/overlays/qemu/patches/0001-qemu-v8.1.3_bpmp-virt.patch create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/overlays/qemu/patches/0001-qemu-v8.1.3_gpio-virt.patch diff --git a/build.sh b/build.sh index 7bc01b127..4ce424a33 100755 --- a/build.sh +++ b/build.sh @@ -12,20 +12,20 @@ case $ans in nixos-rebuild --flake ${flake} build ;; t|T) - echo "Switchng a nix derivation..." - sudo nixos-rebuild --flake ${flake} switch + echo "Testing a nix derivation..." + sudo nixos-rebuild --flake ${flake} test ;; s|S) echo "Switchng a nix derivation..." sudo nixos-rebuild --flake ${flake} switch ;; x|X) - echo "Building a nix derivation..." + echo "Building a nix derivation with trace..." nixos-rebuild --flake ${flake} build --show-trace ;; e|E) echo "Evaluating to eval_${flake}.tmp" - nix derivation show --recursive ${flake} > eval_${flake}.tmp + nix derivation show --show-trace --recursive ${flake} > eval_${flake}.tmp ;; n|N) echo "Exiting..." diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix index f82aa4273..bf407d61c 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix @@ -5,11 +5,12 @@ config, ... }: let - cfg = config.ghaf.hardware.nvidia.virtualization; + cfg = config.ghaf.hardware.nvidia.virtualization.host.bpmp; in { config = lib.mkIf cfg.enable { boot.kernelPatches = [ /* configure kernel in modules/hardware/nvidia-jetson-orin/virtualization/default.nix for all virtualisation + * TODO: differentiate config { name = "Added Configurations to Support Vda"; patch = null; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix index fb6b0f2ef..a9bf5bf17 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix @@ -5,11 +5,12 @@ config, ... }: let - cfg = config.ghaf.hardware.nvidia.virtualization; + cfg = config.ghaf.hardware.nvidia.virtualization.host.gpio; in { config = lib.mkIf cfg.enable { boot.kernelPatches = [ /* configure kernel in modules/hardware/nvidia-jetson-orin/virtualization/default.nix for all virtualisation + * TODO: differentiate config { name = "Added Configurations to Support GPIO passthrough"; patch = null; @@ -31,21 +32,25 @@ in { TEGRA_GPIO_HOST_PROXY = lib.mkDefault lib.kernel.yes; }; } - */ + */ + /* This patch is not needed because of the kernel parameters { name = "Vfio_platform Reset Required False"; patch = ./patches/0002-vfio_platform-reset-required-false.patch; } + */ + # patching the kernel for gpio passthrough { name = "GPIO Support Virtualization"; patch = ./patches/0003-gpio-virt-kernel.patch; } + # patching the custom GPIO kernel modules { name = "GPIO Virt Drivers"; patch = ./patches/0004-gpio-virt-drivers.patch; } /* - # the driver is implemeted as an overlay file not a patch file -- remove patch file + # the driver is implemeted as an overlay file not a patch file -- don't use patch file { name = "GPIO Overlay"; patch = ./patches/0005-gpio-overlay.patch; # source file patch diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/default.nix new file mode 100644 index 000000000..938fc3fab --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/default.nix @@ -0,0 +1,11 @@ +# Copyright 2022-2023 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +(_final: prev: { + qemu = prev.qemu.overrideAttrs (_final: prev: { + patches = + prev.patches + ++ [ + ./patches/0001-qemu-v8.1.3_bpmp-virt.patch + ]; + }); +}) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/qemu/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/qemu/default.nix new file mode 100644 index 000000000..eb4037d74 --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/qemu/default.nix @@ -0,0 +1,11 @@ +# Copyright 2022-2023 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +(_final: prev: { + qemu = prev.qemu.overrideAttrs (_final: prev: { + patches = + prev.patches + ++ [ + ./patches/0001-qemu-v8.1.3_gpio-virt.patch + ]; + }); +}) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/qemu/patches/0001-qemu-v8.1.3_bpmp-virt.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/qemu/patches/0001-qemu-v8.1.3_bpmp-virt.patch new file mode 100644 index 000000000..356e8c01a --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/qemu/patches/0001-qemu-v8.1.3_bpmp-virt.patch @@ -0,0 +1,357 @@ +From e7d0fd8ca9f4334ee5d980217044e9c1c7cc21f4 Mon Sep 17 00:00:00 2001 +From: Mika Tammi +Date: Sun, 7 Jan 2024 00:24:33 +0200 +Subject: Rebased patch on top of QEMU 8.1.3 + +--- + README.md | 50 +++++++++++ + configure.sh | 1 + + hw/arm/Kconfig | 1 + + hw/arm/virt.c | 5 ++ + hw/misc/Kconfig | 3 + + hw/misc/meson.build | 1 + + hw/misc/nvidia_bpmp_guest.c | 171 ++++++++++++++++++++++++++++++++++++ + hw/misc/nvidia_bpmp_guest.h | 9 ++ + include/hw/arm/virt.h | 1 + + 9 files changed, 242 insertions(+) + create mode 100644 README.md + create mode 100755 configure.sh + create mode 100644 hw/misc/nvidia_bpmp_guest.c + create mode 100644 hw/misc/nvidia_bpmp_guest.h + +diff --git a/README.md b/README.md +new file mode 100644 +index 0000000000..56b9e765ec +--- /dev/null ++++ b/README.md +@@ -0,0 +1,50 @@ ++ ++# Short intstructions: ++ ++ ++1. git clone https://gitlab.com/qemu-project/qemu.git ++2 cd qemu ++3 git checkout -b v8.1.3 ++4 ./configure --target-list=aarm64-softmmu ++5 make -j12 ++ ++ ++ ++Device memory map: ++ ++0x090c0000 + /* Base address, size 0x01000 */ ++ ++ 0x0000 \ Tx buffer ++ 0x01FF / ++ 0x0200 \ Rx buffer ++ 0x03FF / ++ 0x0400 -- Tx size ++ 0x0408 -- Rx size ++ 0x0410 -- Ret ++ 0x0500 -- mrq ++ ++ ++ ++ Data should be aligned to 64bit paragraph. ++ ++ Protocol is: ++ 1. Write data buffers to 0x0000-0x01FF and 0x0200-0x03FF ++ 2. Write buffer sizes to 0x0400 (Tx) and 0x0408 (Rx) ++ 2. Start operation by writing mrq opcode to address 0x0500 ++ 3. Read ret code from 0x0410 and response data from the buffers ++ ++ ++For reading and writing busybox may be used as: ++ ++ busybox devmem 0x090c0000 ++ ++and so on ++ ++For instance, to reset the UARTA (with ID 0x64) you can sed the next ++command: ++ ++ busybox devmem 0x090c0000 64 0x0000006400000001 ++ busybox devmem 0x090c0400 8 0x08 ++ busybox devmem 0x090c0500 8 0x14 ++ ++``` +diff --git a/configure.sh b/configure.sh +new file mode 100755 +index 0000000000..cb82ca69a7 +--- /dev/null ++++ b/configure.sh +@@ -0,0 +1 @@ ++./configure --target-list=aarch64-softmmu --enable-sdl --enable-gtk -enable-vhost-kernel --enable-vhost-net --enable-vhost-user --enable-vhost-user-blk-server --enable-vfio-user-server --enable-vhost-crypto --enable-vhost-vdpa --enable-virglrenderer --enable-virtfs --enable-virtfs-proxy-helper +diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig +index 7e68348440..3653c77f9e 100644 +--- a/hw/arm/Kconfig ++++ b/hw/arm/Kconfig +@@ -32,6 +32,7 @@ config ARM_VIRT + select VIRTIO_MEM_SUPPORTED + select ACPI_CXL + select ACPI_HMAT ++ select NVIDIA_BPMP_GUEST + + config CHEETAH + bool +diff --git a/hw/arm/virt.c b/hw/arm/virt.c +index 7d9dbc2663..5ee4ec077a 100644 +--- a/hw/arm/virt.c ++++ b/hw/arm/virt.c +@@ -79,6 +79,7 @@ + #include "hw/virtio/virtio-md-pci.h" + #include "hw/virtio/virtio-iommu.h" + #include "hw/char/pl011.h" ++#include "hw/misc/nvidia_bpmp_guest.h" + #include "qemu/guest-random.h" + + #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ +@@ -155,6 +156,7 @@ static const MemMapEntry base_memmap[] = { + [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, + [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, + [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, ++ [VIRT_NVIDIA_BPMP_GUEST] = { 0x090c0000, 0x00001000 }, + [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, + /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ + [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, +@@ -1046,6 +1048,9 @@ static void create_virtio_devices(const VirtMachineState *vms) + hwaddr size = vms->memmap[VIRT_MMIO].size; + MachineState *ms = MACHINE(vms); + ++ /* Create NVIDIA BPMP guest passthru device, possibly need update ftd - WIP */ ++ nvidia_bpmp_guest_create(vms->memmap[VIRT_NVIDIA_BPMP_GUEST].base); ++ + /* We create the transports in forwards order. Since qbus_realize() + * prepends (not appends) new child buses, the incrementing loop below will + * create a list of virtio-mmio buses with decreasing base addresses. +diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig +index 6996d265e4..30b4c34e69 100644 +--- a/hw/misc/Kconfig ++++ b/hw/misc/Kconfig +@@ -11,6 +11,9 @@ config ARMSSE_MHU + config ARMSSE_CPU_PWRCTRL + bool + ++config NVIDIA_BPMP_GUEST ++ bool ++ + config ISA_DEBUG + bool + depends on ISA_BUS +diff --git a/hw/misc/meson.build b/hw/misc/meson.build +index 892f8b91c5..f623cf2c9c 100644 +--- a/hw/misc/meson.build ++++ b/hw/misc/meson.build +@@ -1,4 +1,5 @@ + system_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c')) ++system_ss.add(when: 'CONFIG_NVIDIA_BPMP_GUEST', if_true: files('nvidia_bpmp_guest.c')) + system_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) + system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) + system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) +diff --git a/hw/misc/nvidia_bpmp_guest.c b/hw/misc/nvidia_bpmp_guest.c +new file mode 100644 +index 0000000000..3facee6d00 +--- /dev/null ++++ b/hw/misc/nvidia_bpmp_guest.c +@@ -0,0 +1,171 @@ ++#include "qemu/osdep.h" ++#include "qemu/log.h" ++#include "qapi/error.h" /* provides error_fatal() handler */ ++#include "hw/sysbus.h" /* provides all sysbus registering func */ ++#include "hw/misc/nvidia_bpmp_guest.h" ++ ++#define TYPE_NVIDIA_BPMP_GUEST "nvidia_bpmp_guest" ++typedef struct NvidiaBpmpGuestState NvidiaBpmpGuestState; ++DECLARE_INSTANCE_CHECKER(NvidiaBpmpGuestState, NVIDIA_BPMP_GUEST, TYPE_NVIDIA_BPMP_GUEST) ++ ++#define TX_BUF 0x0000 ++#define RX_BUF 0x0200 ++#define TX_SIZ 0x0400 ++#define RX_SIZ 0x0408 ++#define RET_COD 0x0410 ++#define MRQ 0x0500 ++ ++#define MEM_SIZE 0x600 ++#define HOST_DEVICE_PATH "/dev/bpmp-host" ++#define MESSAGE_SIZE 0x0200 ++ ++// qemu_log_mask(LOG_UNIMP, "%s: \n", __func__ ); ++ ++struct NvidiaBpmpGuestState ++{ ++ SysBusDevice parent_obj; ++ MemoryRegion iomem; ++ int host_device_fd; ++ uint8_t mem[MEM_SIZE]; ++}; ++ ++// Device memory map: ++ ++// 0x090c0000 + /* Base address, size 0x01000 */ ++ ++// 0x0000 \ Tx buffer ++// 0x01FF / ++// 0x0200 \ Rx buffer ++// 0x03FF / ++// 0x0400 -- Tx size ++// 0x0408 -- Rx size ++// 0x0410 -- Ret ++// 0x0500 -- mrq ++ ++ ++ ++// Data should be aligned to 64bit paragraph. ++ ++// Protocol is: ++// 1. Write data buffers to 0x0000-0x01FF and 0x0200-0x03FF ++// 2. Write buffer sizes to 0x0400 (Tx) and 0x0408 (Rx) ++// 2. Start operation by writing mrq opcode to address 0x0500 ++// 3. Read ret code from 0x0410 and response data from the buffers ++ ++static uint64_t nvidia_bpmp_guest_read(void *opaque, hwaddr addr, unsigned int size) ++{ ++ NvidiaBpmpGuestState *s = opaque; ++ ++ if (addr >= MEM_SIZE) ++ return 0xDEADBEEF; ++ ++ // Cast buffer location as uint64_t ++ return *(uint64_t*)&s->mem[addr]; ++} ++ ++static void nvidia_bpmp_guest_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) ++{ ++ NvidiaBpmpGuestState *s = opaque; ++ int ret; ++ ++ struct ++ { ++ unsigned int mrq; ++ struct ++ { ++ void *data; ++ size_t size; ++ } tx; ++ struct ++ { ++ void *data; ++ size_t size; ++ int ret; ++ } rx; ++ } messg; ++ ++ memset(&messg, 0, sizeof(messg)); ++ ++ if (addr >= MEM_SIZE){ ++ qemu_log_mask(LOG_UNIMP, "qemu: Error addr >= MEM_SIZE in 0x%lX data: 0x%lX\n", addr, data); ++ return; ++ } ++ ++ switch (addr) ++ { ++ case MRQ: ++ // set up the structure ++ messg.mrq = data; ++ messg.tx.data = &s->mem[TX_BUF]; ++ memcpy(&messg.tx.size, &s->mem[TX_SIZ], sizeof(messg.tx.size)); ++ messg.rx.data = &s->mem[RX_BUF]; ++ memcpy(&messg.rx.size, &s->mem[RX_SIZ], sizeof(messg.rx.size)); ++ ++ ret = write(s->host_device_fd, &messg, sizeof(messg)); // Send the data to the host module ++ if (ret < 0) ++ { ++ qemu_log_mask(LOG_UNIMP, "%s: Failed to write the host device..\n", __func__); ++ return; ++ } ++ ++ memcpy(&s->mem[RET_COD], &messg.rx.ret, sizeof(messg.rx.ret)); ++ memcpy(&s->mem[RX_SIZ], &messg.rx.size, sizeof(messg.rx.size)); ++ ++ break; ++ ++ default: ++ ++ memcpy(&s->mem[addr], &data, size); ++ } ++ ++ return; ++} ++ ++static const MemoryRegionOps nvidia_bpmp_guest_ops = { ++ .read = nvidia_bpmp_guest_read, ++ .write = nvidia_bpmp_guest_write, ++ .endianness = DEVICE_NATIVE_ENDIAN, ++}; ++ ++static void nvidia_bpmp_guest_instance_init(Object *obj) ++{ ++ NvidiaBpmpGuestState *s = NVIDIA_BPMP_GUEST(obj); ++ ++ /* allocate memory map region */ ++ memory_region_init_io(&s->iomem, obj, &nvidia_bpmp_guest_ops, s, TYPE_NVIDIA_BPMP_GUEST, MEM_SIZE); ++ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); ++ ++ s->host_device_fd = open(HOST_DEVICE_PATH, O_RDWR); // Open the device with read/write access ++ ++ if (s->host_device_fd < 0) ++ { ++ qemu_log_mask(LOG_UNIMP, "%s: Failed to open the host device..\n", __func__); ++ return; ++ } ++} ++ ++/* create a new type to define the info related to our device */ ++static const TypeInfo nvidia_bpmp_guest_info = { ++ .name = TYPE_NVIDIA_BPMP_GUEST, ++ .parent = TYPE_SYS_BUS_DEVICE, ++ .instance_size = sizeof(NvidiaBpmpGuestState), ++ .instance_init = nvidia_bpmp_guest_instance_init, ++}; ++ ++static void nvidia_bpmp_guest_register_types(void) ++{ ++ type_register_static(&nvidia_bpmp_guest_info); ++} ++ ++type_init(nvidia_bpmp_guest_register_types) ++ ++ /* ++ * Create the Nvidia BPMP guest device. ++ */ ++ DeviceState *nvidia_bpmp_guest_create(hwaddr addr) ++{ ++ DeviceState *dev = qdev_new(TYPE_NVIDIA_BPMP_GUEST); ++ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); ++ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); ++ return dev; ++} +diff --git a/hw/misc/nvidia_bpmp_guest.h b/hw/misc/nvidia_bpmp_guest.h +new file mode 100644 +index 0000000000..dd4b4221d3 +--- /dev/null ++++ b/hw/misc/nvidia_bpmp_guest.h +@@ -0,0 +1,9 @@ ++#ifndef HW_NVIDIA_BPMP_GUEST_H ++#define HW_NVIDIA_BPMP_GUEST_H ++ ++#include "qom/object.h" ++ ++DeviceState *nvidia_bpmp_guest_create(hwaddr); ++ ++ ++#endif +diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h +index e1ddbea96b..fce00ae7e9 100644 +--- a/include/hw/arm/virt.h ++++ b/include/hw/arm/virt.h +@@ -77,6 +77,7 @@ enum { + VIRT_PCIE_MMIO, + VIRT_PCIE_PIO, + VIRT_PCIE_ECAM, ++ VIRT_NVIDIA_BPMP_GUEST, + VIRT_PLATFORM_BUS, + VIRT_GPIO, + VIRT_SECURE_UART, +-- +2.42.0 + diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/qemu/patches/0001-qemu-v8.1.3_gpio-virt.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/qemu/patches/0001-qemu-v8.1.3_gpio-virt.patch new file mode 100644 index 000000000..356e8c01a --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/qemu/patches/0001-qemu-v8.1.3_gpio-virt.patch @@ -0,0 +1,357 @@ +From e7d0fd8ca9f4334ee5d980217044e9c1c7cc21f4 Mon Sep 17 00:00:00 2001 +From: Mika Tammi +Date: Sun, 7 Jan 2024 00:24:33 +0200 +Subject: Rebased patch on top of QEMU 8.1.3 + +--- + README.md | 50 +++++++++++ + configure.sh | 1 + + hw/arm/Kconfig | 1 + + hw/arm/virt.c | 5 ++ + hw/misc/Kconfig | 3 + + hw/misc/meson.build | 1 + + hw/misc/nvidia_bpmp_guest.c | 171 ++++++++++++++++++++++++++++++++++++ + hw/misc/nvidia_bpmp_guest.h | 9 ++ + include/hw/arm/virt.h | 1 + + 9 files changed, 242 insertions(+) + create mode 100644 README.md + create mode 100755 configure.sh + create mode 100644 hw/misc/nvidia_bpmp_guest.c + create mode 100644 hw/misc/nvidia_bpmp_guest.h + +diff --git a/README.md b/README.md +new file mode 100644 +index 0000000000..56b9e765ec +--- /dev/null ++++ b/README.md +@@ -0,0 +1,50 @@ ++ ++# Short intstructions: ++ ++ ++1. git clone https://gitlab.com/qemu-project/qemu.git ++2 cd qemu ++3 git checkout -b v8.1.3 ++4 ./configure --target-list=aarm64-softmmu ++5 make -j12 ++ ++ ++ ++Device memory map: ++ ++0x090c0000 + /* Base address, size 0x01000 */ ++ ++ 0x0000 \ Tx buffer ++ 0x01FF / ++ 0x0200 \ Rx buffer ++ 0x03FF / ++ 0x0400 -- Tx size ++ 0x0408 -- Rx size ++ 0x0410 -- Ret ++ 0x0500 -- mrq ++ ++ ++ ++ Data should be aligned to 64bit paragraph. ++ ++ Protocol is: ++ 1. Write data buffers to 0x0000-0x01FF and 0x0200-0x03FF ++ 2. Write buffer sizes to 0x0400 (Tx) and 0x0408 (Rx) ++ 2. Start operation by writing mrq opcode to address 0x0500 ++ 3. Read ret code from 0x0410 and response data from the buffers ++ ++ ++For reading and writing busybox may be used as: ++ ++ busybox devmem 0x090c0000 ++ ++and so on ++ ++For instance, to reset the UARTA (with ID 0x64) you can sed the next ++command: ++ ++ busybox devmem 0x090c0000 64 0x0000006400000001 ++ busybox devmem 0x090c0400 8 0x08 ++ busybox devmem 0x090c0500 8 0x14 ++ ++``` +diff --git a/configure.sh b/configure.sh +new file mode 100755 +index 0000000000..cb82ca69a7 +--- /dev/null ++++ b/configure.sh +@@ -0,0 +1 @@ ++./configure --target-list=aarch64-softmmu --enable-sdl --enable-gtk -enable-vhost-kernel --enable-vhost-net --enable-vhost-user --enable-vhost-user-blk-server --enable-vfio-user-server --enable-vhost-crypto --enable-vhost-vdpa --enable-virglrenderer --enable-virtfs --enable-virtfs-proxy-helper +diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig +index 7e68348440..3653c77f9e 100644 +--- a/hw/arm/Kconfig ++++ b/hw/arm/Kconfig +@@ -32,6 +32,7 @@ config ARM_VIRT + select VIRTIO_MEM_SUPPORTED + select ACPI_CXL + select ACPI_HMAT ++ select NVIDIA_BPMP_GUEST + + config CHEETAH + bool +diff --git a/hw/arm/virt.c b/hw/arm/virt.c +index 7d9dbc2663..5ee4ec077a 100644 +--- a/hw/arm/virt.c ++++ b/hw/arm/virt.c +@@ -79,6 +79,7 @@ + #include "hw/virtio/virtio-md-pci.h" + #include "hw/virtio/virtio-iommu.h" + #include "hw/char/pl011.h" ++#include "hw/misc/nvidia_bpmp_guest.h" + #include "qemu/guest-random.h" + + #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ +@@ -155,6 +156,7 @@ static const MemMapEntry base_memmap[] = { + [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, + [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, + [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, ++ [VIRT_NVIDIA_BPMP_GUEST] = { 0x090c0000, 0x00001000 }, + [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, + /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ + [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, +@@ -1046,6 +1048,9 @@ static void create_virtio_devices(const VirtMachineState *vms) + hwaddr size = vms->memmap[VIRT_MMIO].size; + MachineState *ms = MACHINE(vms); + ++ /* Create NVIDIA BPMP guest passthru device, possibly need update ftd - WIP */ ++ nvidia_bpmp_guest_create(vms->memmap[VIRT_NVIDIA_BPMP_GUEST].base); ++ + /* We create the transports in forwards order. Since qbus_realize() + * prepends (not appends) new child buses, the incrementing loop below will + * create a list of virtio-mmio buses with decreasing base addresses. +diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig +index 6996d265e4..30b4c34e69 100644 +--- a/hw/misc/Kconfig ++++ b/hw/misc/Kconfig +@@ -11,6 +11,9 @@ config ARMSSE_MHU + config ARMSSE_CPU_PWRCTRL + bool + ++config NVIDIA_BPMP_GUEST ++ bool ++ + config ISA_DEBUG + bool + depends on ISA_BUS +diff --git a/hw/misc/meson.build b/hw/misc/meson.build +index 892f8b91c5..f623cf2c9c 100644 +--- a/hw/misc/meson.build ++++ b/hw/misc/meson.build +@@ -1,4 +1,5 @@ + system_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c')) ++system_ss.add(when: 'CONFIG_NVIDIA_BPMP_GUEST', if_true: files('nvidia_bpmp_guest.c')) + system_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) + system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) + system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) +diff --git a/hw/misc/nvidia_bpmp_guest.c b/hw/misc/nvidia_bpmp_guest.c +new file mode 100644 +index 0000000000..3facee6d00 +--- /dev/null ++++ b/hw/misc/nvidia_bpmp_guest.c +@@ -0,0 +1,171 @@ ++#include "qemu/osdep.h" ++#include "qemu/log.h" ++#include "qapi/error.h" /* provides error_fatal() handler */ ++#include "hw/sysbus.h" /* provides all sysbus registering func */ ++#include "hw/misc/nvidia_bpmp_guest.h" ++ ++#define TYPE_NVIDIA_BPMP_GUEST "nvidia_bpmp_guest" ++typedef struct NvidiaBpmpGuestState NvidiaBpmpGuestState; ++DECLARE_INSTANCE_CHECKER(NvidiaBpmpGuestState, NVIDIA_BPMP_GUEST, TYPE_NVIDIA_BPMP_GUEST) ++ ++#define TX_BUF 0x0000 ++#define RX_BUF 0x0200 ++#define TX_SIZ 0x0400 ++#define RX_SIZ 0x0408 ++#define RET_COD 0x0410 ++#define MRQ 0x0500 ++ ++#define MEM_SIZE 0x600 ++#define HOST_DEVICE_PATH "/dev/bpmp-host" ++#define MESSAGE_SIZE 0x0200 ++ ++// qemu_log_mask(LOG_UNIMP, "%s: \n", __func__ ); ++ ++struct NvidiaBpmpGuestState ++{ ++ SysBusDevice parent_obj; ++ MemoryRegion iomem; ++ int host_device_fd; ++ uint8_t mem[MEM_SIZE]; ++}; ++ ++// Device memory map: ++ ++// 0x090c0000 + /* Base address, size 0x01000 */ ++ ++// 0x0000 \ Tx buffer ++// 0x01FF / ++// 0x0200 \ Rx buffer ++// 0x03FF / ++// 0x0400 -- Tx size ++// 0x0408 -- Rx size ++// 0x0410 -- Ret ++// 0x0500 -- mrq ++ ++ ++ ++// Data should be aligned to 64bit paragraph. ++ ++// Protocol is: ++// 1. Write data buffers to 0x0000-0x01FF and 0x0200-0x03FF ++// 2. Write buffer sizes to 0x0400 (Tx) and 0x0408 (Rx) ++// 2. Start operation by writing mrq opcode to address 0x0500 ++// 3. Read ret code from 0x0410 and response data from the buffers ++ ++static uint64_t nvidia_bpmp_guest_read(void *opaque, hwaddr addr, unsigned int size) ++{ ++ NvidiaBpmpGuestState *s = opaque; ++ ++ if (addr >= MEM_SIZE) ++ return 0xDEADBEEF; ++ ++ // Cast buffer location as uint64_t ++ return *(uint64_t*)&s->mem[addr]; ++} ++ ++static void nvidia_bpmp_guest_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) ++{ ++ NvidiaBpmpGuestState *s = opaque; ++ int ret; ++ ++ struct ++ { ++ unsigned int mrq; ++ struct ++ { ++ void *data; ++ size_t size; ++ } tx; ++ struct ++ { ++ void *data; ++ size_t size; ++ int ret; ++ } rx; ++ } messg; ++ ++ memset(&messg, 0, sizeof(messg)); ++ ++ if (addr >= MEM_SIZE){ ++ qemu_log_mask(LOG_UNIMP, "qemu: Error addr >= MEM_SIZE in 0x%lX data: 0x%lX\n", addr, data); ++ return; ++ } ++ ++ switch (addr) ++ { ++ case MRQ: ++ // set up the structure ++ messg.mrq = data; ++ messg.tx.data = &s->mem[TX_BUF]; ++ memcpy(&messg.tx.size, &s->mem[TX_SIZ], sizeof(messg.tx.size)); ++ messg.rx.data = &s->mem[RX_BUF]; ++ memcpy(&messg.rx.size, &s->mem[RX_SIZ], sizeof(messg.rx.size)); ++ ++ ret = write(s->host_device_fd, &messg, sizeof(messg)); // Send the data to the host module ++ if (ret < 0) ++ { ++ qemu_log_mask(LOG_UNIMP, "%s: Failed to write the host device..\n", __func__); ++ return; ++ } ++ ++ memcpy(&s->mem[RET_COD], &messg.rx.ret, sizeof(messg.rx.ret)); ++ memcpy(&s->mem[RX_SIZ], &messg.rx.size, sizeof(messg.rx.size)); ++ ++ break; ++ ++ default: ++ ++ memcpy(&s->mem[addr], &data, size); ++ } ++ ++ return; ++} ++ ++static const MemoryRegionOps nvidia_bpmp_guest_ops = { ++ .read = nvidia_bpmp_guest_read, ++ .write = nvidia_bpmp_guest_write, ++ .endianness = DEVICE_NATIVE_ENDIAN, ++}; ++ ++static void nvidia_bpmp_guest_instance_init(Object *obj) ++{ ++ NvidiaBpmpGuestState *s = NVIDIA_BPMP_GUEST(obj); ++ ++ /* allocate memory map region */ ++ memory_region_init_io(&s->iomem, obj, &nvidia_bpmp_guest_ops, s, TYPE_NVIDIA_BPMP_GUEST, MEM_SIZE); ++ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); ++ ++ s->host_device_fd = open(HOST_DEVICE_PATH, O_RDWR); // Open the device with read/write access ++ ++ if (s->host_device_fd < 0) ++ { ++ qemu_log_mask(LOG_UNIMP, "%s: Failed to open the host device..\n", __func__); ++ return; ++ } ++} ++ ++/* create a new type to define the info related to our device */ ++static const TypeInfo nvidia_bpmp_guest_info = { ++ .name = TYPE_NVIDIA_BPMP_GUEST, ++ .parent = TYPE_SYS_BUS_DEVICE, ++ .instance_size = sizeof(NvidiaBpmpGuestState), ++ .instance_init = nvidia_bpmp_guest_instance_init, ++}; ++ ++static void nvidia_bpmp_guest_register_types(void) ++{ ++ type_register_static(&nvidia_bpmp_guest_info); ++} ++ ++type_init(nvidia_bpmp_guest_register_types) ++ ++ /* ++ * Create the Nvidia BPMP guest device. ++ */ ++ DeviceState *nvidia_bpmp_guest_create(hwaddr addr) ++{ ++ DeviceState *dev = qdev_new(TYPE_NVIDIA_BPMP_GUEST); ++ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); ++ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); ++ return dev; ++} +diff --git a/hw/misc/nvidia_bpmp_guest.h b/hw/misc/nvidia_bpmp_guest.h +new file mode 100644 +index 0000000000..dd4b4221d3 +--- /dev/null ++++ b/hw/misc/nvidia_bpmp_guest.h +@@ -0,0 +1,9 @@ ++#ifndef HW_NVIDIA_BPMP_GUEST_H ++#define HW_NVIDIA_BPMP_GUEST_H ++ ++#include "qom/object.h" ++ ++DeviceState *nvidia_bpmp_guest_create(hwaddr); ++ ++ ++#endif +diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h +index e1ddbea96b..fce00ae7e9 100644 +--- a/include/hw/arm/virt.h ++++ b/include/hw/arm/virt.h +@@ -77,6 +77,7 @@ enum { + VIRT_PCIE_MMIO, + VIRT_PCIE_PIO, + VIRT_PCIE_ECAM, ++ VIRT_NVIDIA_BPMP_GUEST, + VIRT_PLATFORM_BUS, + VIRT_GPIO, + VIRT_SECURE_UART, +-- +2.42.0 + diff --git a/modules/microvm/virtualization/microvm/gpiovm.nix b/modules/microvm/virtualization/microvm/gpiovm.nix index bf6281fc9..10509bedb 100644 --- a/modules/microvm/virtualization/microvm/gpiovm.nix +++ b/modules/microvm/virtualization/microvm/gpiovm.nix @@ -6,7 +6,6 @@ pkgs, ... } : with pkgs; let - cfg = config.ghaf.virtualization.microvm.gpiovm; configHost = config; vmName = "gpio-vm"; @@ -18,6 +17,13 @@ users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; development = { debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; + nix-setup.enable = lib.mkDefault configHost.ghaf.development.nix-setup.enable; + }; + systemd = { + enable = true; + withName = "gpiovm-systemd"; + withPolkit = true; + withDebug = configHost.ghaf.profiles.debug.enable; }; }; @@ -28,11 +34,12 @@ microvm.hypervisor = "qemu"; - /* add services in extraModules variable instead + /* services.xxx = { - enable = true; - }; + # we define a servce in extraModules variable below with import ./gpio-test.nix + } */ + microvm = { optimize.enable = true; shares = [ @@ -45,10 +52,11 @@ writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; }; - imports = import ../../module-list.nix; + imports = [../../../common]; }) ]; }; + cfg = config.ghaf.virtualization.microvm.gpiovm; in { options.ghaf.virtualization.microvm.gpiovm = { enable = lib.mkEnableOption "GPIO-VM"; @@ -63,7 +71,6 @@ in { }; }; - config = lib.mkIf cfg.enable { microvm.vms."${vmName}" = { autostart = true; @@ -74,7 +81,7 @@ in { gpiovmBaseConfiguration.imports ++ cfg.extraModules; }; - specialArgs = {inherit lib;}; + # specialArgs = {inherit lib;}; }; }; } diff --git a/targets/nvidia-jetson-orin/flake-module.nix b/targets/nvidia-jetson-orin/flake-module.nix index 095721617..db197c639 100644 --- a/targets/nvidia-jetson-orin/flake-module.nix +++ b/targets/nvidia-jetson-orin/flake-module.nix @@ -32,6 +32,11 @@ }; } ]; + gpiovmExtraModules = [ + { + # hardware modules for gpio + } + ]; hostConfiguration = lib.nixosSystem { inherit system; @@ -47,7 +52,6 @@ self.nixosModules.jetpack self.nixosModules.jetpack-microvm self.nixosModules.microvm - { ghaf = { hardware.nvidia.orin = { @@ -55,12 +59,17 @@ somType = som; agx.enableNetvmWlanPCIPassthrough = som == "agx"; nx.enableNetvmEthernetPCIPassthrough = som == "nx"; + agx.enableGPIOPassthrough = som == "agx"; }; hardware.nvidia = { - virtualization.enable = false; - virtualization.host.bpmp.enable = false; - passthroughs.host.uarta.enable = false; + virtualization.enable = lib.mkDefault true; + virtualization.host.bpmp.enable = lib.mkDefault false; + passthroughs.host.uarta.enable = lib.mkDefault false; + virtualization.host.gpio.enable = lib.mkDefault true; + # virtualization.enable = false; + # virtualization.host.bpmp.enable = false; + # passthroughs.host.uarta.enable = false; }; virtualization.microvm-host.enable = true; @@ -69,6 +78,8 @@ virtualization.microvm.netvm.enable = true; virtualization.microvm.netvm.extraModules = netvmExtraModules; + virtualization.microvm.gpiovm.enable = true; + virtualization.microvm.gpiovm.extraModules = gpiovmExtraModules; # Enable all the default UI applications profiles = { From 3f52d3553f36f5f0990b8b5cebe89c1bf7ade4e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Fri, 12 Apr 2024 07:18:14 +0000 Subject: [PATCH 06/26] Towards a working gpio-vmi The vda device is removed from the gpio-vm because it will not work while we do not have the proper Device Tree in the VM. --- .../agx-gpiovm-passthrough.nix | 49 +++---------------- .../agx-netvm-wlan-pci-passthrough.nix | 2 +- .../host/gpio-virt-host/default.nix | 3 +- .../microvm/virtualization/microvm/gpiovm.nix | 45 +++++++++++++++-- targets/nvidia-jetson-orin/flake-module.nix | 2 +- 5 files changed, 52 insertions(+), 49 deletions(-) diff --git a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix index 249b5e764..8544a538a 100644 --- a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix +++ b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix @@ -26,53 +26,18 @@ in { microvm.kernelParams = [ "rootwait" - "root=/dev/vda" + # "root=/dev/vda" "console=ttyAMA0" ]; } ]; - /* - # config for both host and guest (we have only one kernel version) - # because of that this config is redundant (an insert for future guest configuration) - boot.kernelPatches.extraStructuredConfig = { - TEGRA_GPIO_HOST_PROXY = lib.kernel.yes; - TEGRA_GPIO_GUEST_PROXY = lib.kernel.yes; - }; - */ - - /* - # No need to set host kernel boot params here ? - boot.kernelParams = [ - "iommu=pt" - "vfio.enable_unsafe_noiommu_mode=0" - "vfio_iommu_type1.allow_unsafe_interrupts=1" - "vfio_platform.reset_required=0" - ]; - */ - - # No need to set host device tree here ??? - /* - hardware.deviceTree = { - # Enable hardware.deviceTree for handle host dtb overlays - enable = true; - - # name = "tegra234-p3701-0000-p3737-0000.dtb"; - # name = "tegra234-p3701-host-passthrough.dtb"; - - # using overlay file: - overlays = [ - { - name = "gpio_pt_host_overlay"; - dtsFile = ./gpio_pt_host_overlay.dtso; - - # Apply overlay only to host passthrough device tree - # filter = "tegra234-gpio-host-proxy.dtb"; - # filter = "tegra234-p3701-0000-p3737-0000.dtb"; - filter = "tegra234-p3701-host-passthrough.dtb"; - } - ]; - }; + /* tmp note: further kernel settings for nvidia in: + ../jetpack/nvidia-jetson-orin/virtualization/default.nix + ../jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix + ../jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix + ../jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix + ../jetpack/nvidia-jetson-orin/virtualization/host/bpmp-virt-host/default.nix */ }; } diff --git a/modules/jetpack-microvm/agx-netvm-wlan-pci-passthrough.nix b/modules/jetpack-microvm/agx-netvm-wlan-pci-passthrough.nix index d6b0eba87..a5a60e237 100644 --- a/modules/jetpack-microvm/agx-netvm-wlan-pci-passthrough.nix +++ b/modules/jetpack-microvm/agx-netvm-wlan-pci-passthrough.nix @@ -37,7 +37,7 @@ in { "vfio_iommu_type1.allow_unsafe_interrupts=1" ]; - hardware.deviceTree = { + hardware.deviceTree = lib.mkDefault { enable = true; name = "tegra234-p3701-host-passthrough.dtb"; }; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix index 76ea7eab1..0f1464fae 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix @@ -39,6 +39,7 @@ in { hardware.deviceTree = { # Enable hardware.deviceTree for handle host dtb overlays enable = true; + name = "tegra234-p3701-0000-p3737-0000"; # name = "tegra234-p3701-0000-p3737-0000.dtb"; # name = "tegra234-p3701-host-passthrough.dtb"; @@ -49,7 +50,7 @@ in { dtsFile = ./gpio_pt_host_overlay.dtso; # Apply overlay only to host passthrough device tree - # filter = "tegra234-p3701-0000-p3737-0000.dtb"; + filter = "tegra234-p3701-0000-p3737-0000.dtb"; # filter = "tegra234-p3701-host-passthrough.dtb"; } ]; diff --git a/modules/microvm/virtualization/microvm/gpiovm.nix b/modules/microvm/virtualization/microvm/gpiovm.nix index 10509bedb..b0e9bec63 100644 --- a/modules/microvm/virtualization/microvm/gpiovm.nix +++ b/modules/microvm/virtualization/microvm/gpiovm.nix @@ -9,9 +9,26 @@ configHost = config; vmName = "gpio-vm"; + /* + # guestdts specifies specifically gpiovm's device tree + # todo synchronise with (move to) definition in ../host/gpio-virt-host/default.nix + dtbSrc = "tegra234-p3701-0000-p3737-0000"; + dtName = "tegra234-p3701-gpio-guestvm"; + # TODO we do not have ./gpio_pt_guest_overlay.dtso yet + dtsFile = ../../..jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/gpio_pt_host_overlay.dtso; + gpioGuestOutput = dtName + ".dtb"; + gpioGuestDtb = new File gpioPtGuestOutput {}; + + # Creating a new DTB file named gpio_pt_guest.dtb + nixpkgs.buildInputs.utils.copy-file { + inputFile = "${sourcePath}/${name}"; + outputFile = gpioPtGuestOutput; + override = true; + }; + */ + gpiovmBaseConfiguration = { imports = [ - # (import ./common/vm-networking.nix {inherit vmName macAddress;}) ({lib, ...}: { ghaf = { users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; @@ -33,13 +50,27 @@ nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system; microvm.hypervisor = "qemu"; + # microvm.hypervisor.extraargs = ["--dtb", dtbfilepath]; /* services.xxx = { # we define a servce in extraModules variable below with import ./gpio-test.nix } */ - + /* + options.hardware.devicetree = { + enable = true; + name = dtName; + overlays = [ + { + name = "gpio_pt_guest_overlay"; + # TODO we do not have ./gpio_pt_guest_overlay.dtso yet + dtsFile = dtsFile; + filter = gpioPtGuestOutput; + } + ]; + }; + */ microvm = { optimize.enable = true; shares = [ @@ -50,6 +81,12 @@ } ]; writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; + + /* + qemu.extraargs = [ + "--dtb" gpioGuestDtb + ]; + */ }; imports = [../../../common]; @@ -59,12 +96,12 @@ cfg = config.ghaf.virtualization.microvm.gpiovm; in { options.ghaf.virtualization.microvm.gpiovm = { - enable = lib.mkEnableOption "GPIO-VM"; + enable = lib.mkEnableOption "gpio-vm"; extraModules = lib.mkOption { description = '' List of additional modules to be imported and evaluated as part of - GPIO-VM's NixOS configuration. + gpio-vm's NixOS configuration. ''; # A service that runs a script to test gpio pins default = [ import ./gpio-test.nix { pkgs = pkgs; } ]; diff --git a/targets/nvidia-jetson-orin/flake-module.nix b/targets/nvidia-jetson-orin/flake-module.nix index db197c639..5b3c201d2 100644 --- a/targets/nvidia-jetson-orin/flake-module.nix +++ b/targets/nvidia-jetson-orin/flake-module.nix @@ -63,7 +63,7 @@ }; hardware.nvidia = { - virtualization.enable = lib.mkDefault true; + # virtualization.enable = lib.mkDefault true; virtualization.host.bpmp.enable = lib.mkDefault false; passthroughs.host.uarta.enable = lib.mkDefault false; virtualization.host.gpio.enable = lib.mkDefault true; From 85fc63d7d4d7c3f918f552cb918d883c4441c5a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Mon, 15 Apr 2024 07:03:39 +0000 Subject: [PATCH 07/26] add documentation for GPIO passthrough --- docs/src/technologies/nvidia_agx_pt_gpio.md | 414 ++++++++++++++++++++ targets/nvidia-jetson-orin/flake-module.nix | 2 +- 2 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 docs/src/technologies/nvidia_agx_pt_gpio.md diff --git a/docs/src/technologies/nvidia_agx_pt_gpio.md b/docs/src/technologies/nvidia_agx_pt_gpio.md new file mode 100644 index 000000000..95ce19c90 --- /dev/null +++ b/docs/src/technologies/nvidia_agx_pt_gpio.md @@ -0,0 +1,414 @@ + + +# NVIDIA Jetson AGX Orin: GPIO Passthrough + +This document describes the GPIO passthrough implementation on the NVIDIA Jetson AGX Orin board. The purpose of GPIO passthrough is to allow a virtual machine to access GPIO via the available GPIO chips. + +## GPIO Chips and Lines + +There are two GPIO chips in Nvidia Jetson AGX controlling GPIO, + +- _tegra234-gpio_, controlling 164 lines + - On /dev/gpiochip0, it is also refered to as "gpio_main" + - (See Appendix A.1) +- _tegra234-gpio-aon_, controlling 32 lines + - On /dev/gpiochip1, it is also refered to as "gpio_aon"and "gpio_main_aon" + - (See Appendix A.2) + +Each GPIO chip controll a number of GPIO lines. _tegra234-gpio_, controls 164 lines and _tegra234-gpio-aon_ controls 32 lines. Each line is a logical GPIO pin and the line number determines the offset from the GPIO chip's base. + +Many of the lines are by default reserved for built in functionality such as regulators, resets, interrupts and camera control (which is using lines for I2S, SPI and other functions) and internal ports such as UART (UARTA or UART1). Some ports are free to use without disrupting the normal operations of Jetson AGX. However reconfiguration of reserved functions is as such possible, but not recommended. +See Appendix A. + +Some lines are brought to the pinout of the Jetson 40-pin header. (See Appendix B) +Note that not all pins on the 40-pin header are controlled by the GPIO chips. Some pins have dedicated driver circuitry, thus not available for GPIO nor for GPIO passthrough. + +## GPIO Passthrough Host and Guest kernel modules + +A kernel driver in Host is acting as a proxy for the Guest VM to operate the GPIO chips and thereby the GPIO lines. This driver is implemented as a built-in kernel module. In Guest the tegra186-gpio kernel driver is hooked to send GPIO request to and receive replies from the /dev/vda device which passes the messages to host-passthrough and the Host's GPIO proxy driver. + + +## Host Device Tree + +To prepare GPIO on the host for the passthrough: + +>Add a virtual node to root in the Device Tree using an overlay file. + A driver associated to this node will find it using the compatible field. +``` + /plugin/; + + /{ + overlay-name = "GPIO passthrough on host"; + compatible = "nvidia,p3737-0000+p3701-0000\0nvidia,tegra234\0nvidia,tegra23x"; + + fragment@0 + { target-path = "/"; + __overlay__ + { gpio_host_proxy + { compatible = "nvidia,gpio-host-proxy"; + status = "okay"; + }; + }; + }; + }; +``` + + +## Guest Device Tree + + The Guest's Device tree serves two purposes. + - to define the _vda_ passthrough device + - to select which lines are allowed for passtrough + +### Creating the Guest Device Tree + +The Guest's Device Tree is based on the device tree extracted from QEMU VM. + +>To get the base QEMU device tree, run the following command: + +``` + qemu-system-aarch64 -machine virt,accel=kvm,dumpdtb=virt.dtb -cpu host +``` + + ### Apply an overlay to define the VDA device +The Device Tree defines passthrough memory for the /dev/vda passthrough device with the parameter _virtual-pa_. +>Add the passthrough device as a root node to the virtual machine's device tree: + +``` + /plugin/; + + /{ + overlay-name = "GPIO passthrough on host"; + compatible = "nvidia,p3737-0000+p3701-0000\0nvidia,tegra234\0nvidia,tegra23x"; + + fragment@0 + { target-path = "/"; + __overlay__ + { + gpio: gpio { + compatible = "nvidia,tegra234-bpmp"; + virtual-pa = <0x0 0x090c0000>; + status = "okay"; + } + }; + }; + }; +``` + +> The *gpio* node was added to the root node. + +### Apply an overlay to define allowed lines for passthrough + +The GPIO lines Selected for Passthrough are defined by the Guest's Device Tree + +>The Device Tree in the Guest virtual machine will determine which Host GPIO pins are visible and usable for the Guest. This is not the only function of the Guest's Device Tree, also see section Guest Device Tree. + +``` + Selecting pins is TODO -- put guest overlay here +``` + + +## Starting the Guest VM + +To start the guest VM: + +1. Set kernel startup paramters on host: +``` + iommu=pt vfio.enable_unsafe_noiommu_mode=0 vfio_iommu_type1.allow_unsafe_interrupts=1 vfio_platform.reset_required=0 +``` +2. Set kernel parameters for guest VM in microvm (or QEMU): + +``` + rootwait root=/dev/vda console=ttyAMA0 +``` +3. Set the device tree for guest according (see section Guest Device Tree) + +## Testing the passtrough + +For testing we need to make the Guest VM use GPIO ports. this can be done either via an UARTA VM console, provided by UARTA passthrough, or by letting the VM's systemd testing service use the ports. + +In both cases GPIO port functionality can be verified from the 40-pin header using a logic analysator connected to the 40-head port. (See Appendix C) + +### Using a VM console over UARTA + +If you use the built in systemd service in Guest to test the GPIO ports you do not need to follow the steps to enable UARTA. In earier versions of Tegra kernel code, patches for UARTA/BPMP and GPIO were conflicting. Also, it is not certain that that UARTA and GPIO passthrough is shared in the same virtual machine in future versions of Ghaf. + +If you are using UARTA as a debug port, stop the microvm@gpio-vm service if it is running. in the VM execute: +``` + sudo systemctl stop microvm@gpio-vm +``` + +1. Connect the NVIDIA Jetson AGX Orin Debug USB to your PC and open the serial port ttyACM1 at 115200 bps. You can use picocom with this command: + +``` + picocom -b 115200 /dev/ttyACM1 +``` +2. When the guest VM is launched you can see the VM Linux command line in the opened ttyACM1 terminal. +3. A script testing the ports vcan be executed or any other CLI commands that set up and use available ports. + +### Using the predefined systemd testing service + +A systemd service called microvm@gpio-vm is enabled in the Guest VM and it starts to execute a testing script. It is set to execute the _simple-chardev-test.sh_ bash script. + +To verify pin functionality. Connect a logic analyser to pins GPIO08, GPIO09, GPIO27, GPIO35 on the 40-pin Jetson header. (See appendix C.) + +## Appendixes + +### Appendix A.1 + +Line is the offset from each gpiochoip's base address. Direciton and comment declare defalult use. + +####gpiochip0 / tegra234-gpio - 164 lines +| gpio line | pin | default direction | comment | +| ---------|-------|--------|----------------------------------------------------| +| line 0 | PA.00 | output | consumer=fixed-regulators:regulator@111 | +| line 1 | PA.01 | output | active-low consumer=fixed-regulators:regulator@114 | +| line 2 | PA.02 | output | | +| line 3 | PA.03 | output | consumer=fixed-regulators:regulator@110 | +| line 4 | PA.04 | input | | +| line 5 | PA.05 | input | | +| line 6 | PA.06 | input | | +| line 7 | PA.07 | input | | +| line 8 | PB.00 | input | | +| line 9 | PC.00 | input | | +| line 10 | PC.01 | input | | +| line 11 | PC.02 | input | | +| line 12 | PC.03 | input | | +| line 13 | PC.04 | input | | +| line 14 | PC.05 | input | | +| line 15 | PC.06 | input | | +| line 16 | PC.07 | input | | +| line 17 | PD.00 | input | | +| line 18 | PD.01 | input | | +| line 19 | PD.02 | input | | +| line 20 | PD.03 | input | | +| line 21 | PE.00 | input | | +| line 22 | PE.01 | input | | +| line 23 | PE.02 | input | | +| line 24 | PE.03 | input | | +| line 25 | PE.04 | input | | +| line 26 | PE.05 | input | | +| line 27 | PE.06 | input | | +| line 28 | PE.07 | input | | +| line 29 | PF.00 | input | | +| line 30 | PF.01 | input | | +| line 31 | PF.02 | input | | +| line 32 | PF.03 | input | | +| line 33 | PF.04 | input | | +| line 34 | PF.05 | input | | +| line 35 | PG.00 | input | active-low consumer=force-recovery | +| line 36 | PG.01 | input | consumer=temp-alert | +| line 37 | PG.02 | input | active-low consumer=sleep | +| line 38 | PG.03 | output | | +| line 39 | PG.04 | input | | +| line 40 | PG.05 | input | | +| line 41 | PG.06 | input | | +| line 42 | PG.07 | input | consumer=cd | +| line 43 | PH.00 | input | GPIO35 | +| line 44 | PH.01 | output | | +| line 45 | PH.02 | input | | +| line 46 | PH.03 | output | consumer=camera-control-output-low | +| line 47 | PH.04 | output | consumer=fixed-regulators:regulator@105 | +| line 48 | PH.05 | output | | +| line 49 | PH.06 | output | consumer=camera-control-output-low | +| line 50 | PH.07 | input | I2S2_CLK | +| line 51 | PI.00 | input | I2S_SDIN | +| line 52 | PI.01 | input | I2S_SDOUT | +| line 53 | PI.02 | input | I2S_FS | +| line 54 | PI.03 | input | | +| line 55 | PI.04 | input | | +| line 56 | PI.05 | input | | +| line 57 | PI.06 | input | | +| line 58 | PJ.00 | input | | +| line 59 | PJ.01 | input | | +| line 60 | PJ.02 | input | | +| line 61 | PJ.03 | input | | +| line 62 | PJ.04 | input | | +| line 63 | PJ.05 | input | | +| line 64 | PK.00 | input | | +| line 65 | PK.01 | input | | +| line 66 | PK.02 | input | | +| line 67 | PK.03 | input | | +| line 68 | PK.04 | input | | +| line 69 | PK.05 | output | | +| line 70 | PK.06 | input | | +| line 71 | PK.07 | input | | +| line 72 | PL.00 | input | | +| line 73 | PL.01 | input | | +| line 74 | PL.02 | input | | +| line 75 | PL.03 | input | | +| line 76 | PM.00 | input | | +| line 77 | PM.01 | input | | +| line 78 | PM.02 | input | | +| line 79 | PM.03 | input | | +| line 80 | PM.04 | input | | +| line 81 | PM.05 | input | | +| line 82 | PM.06 | input | | +| line 83 | PM.07 | input | | +| line 84 | PN.00 | input | | +| line 85 | PN.01 | input | GPIO27 | +| line 86 | PN.02 | input | | +| line 87 | PN.03 | output | | +| line 88 | PN.04 | input | | +| line 89 | PN.05 | input | | +| line 90 | PN.06 | input | | +| line 91 | PN.07 | input | | +| line 92 | PP.00 | input | | +| line 93 | PP.01 | input | | +| line 94 | PP.02 | input | | +| line 95 | PP.03 | input | | +| line 96 | PP.04 | input | GPIO17 | +| line 97 | PP.05 | input | | +| line 98 | PP.06 | input | | +| line 99 | PP.07 | input | | +| line 100 | PQ.00 | input | | +| line 101 | PQ.01 | output | consumer=fixed-regulators:regulator@106 | +| line 102 | PQ.02 | input | | +| line 103 | PQ.03 | input | | +| line 104 | PQ.04 | output | | +| line 105 | PQ.05 | input | | +| line 106 | PQ.06 | input | MCLK05 | +| line 107 | PQ.07 | input | | +| line 108 | PR.00 | input | GPIO32 | +| line 109 | PR.01 | input | | +| line 110 | PR.02 | input | | +| line 111 | PR.03 | input | | +| line 112 | PR.04 | input | UART1_RTS | +| line 113 | PR.05 | input | UART1_CTS | +| line 114 | PX.00 | input | | +| line 115 | PX.01 | input | | +| line 116 | PX.02 | input | | +| line 117 | PX.03 | input | | +| line 118 | PX.04 | input | | +| line 119 | PX.05 | input | | +| line 120 | PX.06 | input | | +| line 121 | PX.07 | input | | +| line 122 | PY.00 | output | | +| line 123 | PY.01 | output | consumer=phy_reset | +| line 124 | PY.02 | output | | +| line 125 | PY.03 | input | consumer=interrupt | +| line 126 | PY.04 | input | consumer=interrupt | +| line 127 | PY.05 | input | | | +| line 128 | PY.06 | input | | +| line 129 | PY.07 | input | | +| line 130 | PZ.00 | output | | +| line 131 | PZ.01 | input | | +| line 132 | PZ.02 | input | | +| line 133 | PZ.03 | input | SPI1_MOSI_SCK | +| line 134 | PZ.04 | input | SPI1_MOSI | +| line 135 | PZ.05 | input | SPI1_MOSI | +| line 136 | PZ.06 | input | SPI1_CS0_N | +| line 137 | PZ.07 | input | SPI1_CS1_N | +| line 138 | PAC.00 | output | consumer=camera-control-output-low | +| line 139 | PAC.01 | output | consumer=camera-control-output-low | +| line 140 | PAC.02 | output | | +| line 141 | PAC.03 | input | | +| line 142 | PAC.04 | input | | +| line 143 | PAC.05 | input | consumer=interrupt | +| line 144 | PAC.06 | input | | +| line 145 | PAC.07 | output | consumer=fixed-regulators:regulator@115 | +| line 146 | PAD.00 | input | | +| line 147 | PAD.01 | input | | +| line 148 | PAD.02 | input | | +| line 149 | PAD.03 | input | | +| line 150 | PAE.00 | input | | +| line 151 | PAE.01 | input | | +| line 152 | PAF.00 | input | | +| line 153 | PAF.01 | input | | +| line 154 | PAF.02 | input | | +| line 155 | PAF.03 | input | | +| line 156 | PAG.00 | input | | +| line 157 | PAG.01 | input | | +| line 158 | PAG.02 | input | | +| line 159 | PAG.03 | input | | +| line 160 | PAG.04 | input | | +| line 161 | PAG.05 | input | | +| line 162 | PAG.06 | input | | +| line 163 | PAG.07 | input | | + +### Appendix A.2 + +Line is the offset from each gpiochoip's base address. Direciton and comment declare defalult use. + +####gpiochip1 / tegra234-gpio-aon - 32 lines: +| gpio line | pin | default direction | comment | +| ---------|-------|--------|----------------------------------------------------| +| line 0 | PAA.00 | input | | +| line 1 | PAA.01 | input | | +| line 2 | PAA.02 | input | | +| line 3 | PAA.03 | input | | +| line 4 | PAA.04 | input | | +| line 5 | PAA.05 | input | | +| line 6 | PAA.06 | input | | +| line 7 | PAA.07 | input | | +| line 8 | PBB.00 | input | GPIO9 | +| line 9 | PBB.01 | input | GPIO8 | +| line 10 | PBB.02 | input | | +| line 11 | PBB.03 | output | | +| line 12 | PCC.00 | input | | +| line 13 | PCC.01 | input | | +| line 14 | PCC.02 | output | consumer=fixed-regulators:regulator@116 | +| line 15 | PCC.03 | input | | +| line 16 | PCC.04 | input | | +| line 17 | PCC.05 | input | | +| line 18 | PCC.06 | input | | +| line 19 | PCC.07 | input | | +| line 20 | PDD.00 | input | | +| line 21 | PDD.01 | input | | +| line 22 | PDD.02 | input | | +| line 23 | PEE.00 | input | | +| line 24 | PEE.01 | input | | +| line 25 | PEE.02 | input | | +| line 26 | PEE.03 | input | | +| line 27 | PEE.04 | input | active-low consumer=power-key | +| line 28 | PEE.05 | input | | +| line 29 | PEE.06 | input | | +| line 30 | PEE.07 | input | | +| line 31 | PGG.00 | input | | + +### Appendix B + +#### Jetson AGX Orin J30 GPIO Expansion Header pinout + +|Line|Sysfs GPIO| Connector Label | Description | More | Pin || Pin | Connector Label | Description | More |Line|Sysfs GPIO| +|----|----------|-----------------|--------------|---------------|------||------|--------------------|---------------|--------|----|----------| +| | | 3.3 VDC | Power | 1A max | **1**|| **2**| 5.0 VDC | Power | 1A max | | | +| | |I2C5_DAT|General I2C5 Data| 1.8/3.3V, I2C Bus 8 | **3**|| **4**| 5.0 VDC | Power | 1A max | | | +| | | 2C_GP5_CLK | General I2C #5 Clock | 1.8/3.3V, I2C Bus 8 | **5**|| **6**| GND | | | | | +|106 | gpio454 | MCLK05 | Audio Master Clock | 1.8/3.3V | **7**|| **8**| UART1_TX | UART #1 |Transmit| | | +| | | GND | | |**9 **||**10**| UART1_RX | UART #1 |Receive | | | +|112 | gpio460 | UART1_RTS | UART #1 Request to Send |1.8/3.3V |**11**||**12**| I2S2_CLK | Audio I2S #2 | Clock | 50 | gpio398 | +|108 | gpio456 | **GPIO32** | GPIO #32 | |**13**||**14**| GND | | | +|85 | gpio433 | **GPIO27** | (PWM) | |**15**||**16**| **GPIO8** | GPIO #8 | | 9 | gpio357 | +| | | 3.3 VDC | Power | 1A max |**17**||**18**| **GPIO35** | (PWM) | | 43 | gpio391 | +|135 | gpio483 | SPI1_MOSI | SPI #1 | Master Out/Slave In |**19**||**20**| GND | | | | | +|134 | gpio482 | SPI1_MOSI | SPI #1 | Master In/Slave Out |**21**||**22**| GPIO17 | GPIO #17 | | 96 | gpio444 | +|133 | gpio481 | SPI1_SCK | SPI #1 | Shift Clock |**23**||**24**| SPI1_CS0_N | SPI #1 |Chip Select #0 | 136| gpio484 | +| | | GND | | |**25**||**26**| SPI1_CS1_N | SPI #1 |Chip Select #1 | 137| gpio485 | +| | | I2C2_DAT | General I2C #2 Data | I2C Bus 1 |**27**||**28**| I2C2_CLK | General I2C #2 Clock | I2C Bus 1 | | | +|1 | gpio317 | CAN0_DIN |CAN #0 | Data In |**29**||**30**| GND | | | | | +|0 | gpio316 | CAN0_DOUT |CAN #0 | Data Out |**31**||**32**| **GPIO9** | GPIO #9 | | 8 | gpio324 | +|2 | gpio318 | CAN1_DOUT |CAN #1 | Data Out |**33**||**34**| GND | | | | | +|53 | gpio401 | I2S_FS | AUDIO I2S #2 Left/Right | Clock |**35**||**36**| UART1_CTS |UART #1 | Clear to Send | 113| gpio461 | +|3 | gpio319 | CAN1_DIN | CAN #1 Data In | |**37**||**38**| I2S_SDIN | Audio I2S #2 |Data In | 52 | gpio400 | +| | | GND | | |**39**||**40**| I2S_SDOUT | Audio I2S #2 |Data Out| 51 | gpio399 | + +On I2C bus 1, there are existing devices on 0x08, 0x40, 0x41. These are denoted as UU by i2cdetect +Default Setup + +The initial pinmux should set all of the these pins, except for the power, UART RX TX and two I2C busses, to GPIO at boot. +Usage designations + +The usages described in the above table is the official NVIDIA suggested pin usage for SFIO functionality. A modified device tree or modification to the appropriate registers is required before using as the described function. +Base GPIO Addresses + + - There are two GPIO bases for the line offset, 316 and 348. + - First number is the GPIO number (i.e. the line offset) within a GPIO controller , compare to Appendix A.1 and A.2 + - Second ( Sysfs GPIO / gpioXXXX ) is the global Linux GPIO number + - Pin 15 – When configured as PWM: + - PWM chip sysfs directory: /sys/devices/3280000.pwm + - Pin 18 – When configured as PWM: + - PWM chip sysfs directory: /sys/devices/32c0000.pwm \ No newline at end of file diff --git a/targets/nvidia-jetson-orin/flake-module.nix b/targets/nvidia-jetson-orin/flake-module.nix index 5b3c201d2..532926038 100644 --- a/targets/nvidia-jetson-orin/flake-module.nix +++ b/targets/nvidia-jetson-orin/flake-module.nix @@ -63,7 +63,7 @@ }; hardware.nvidia = { - # virtualization.enable = lib.mkDefault true; + virtualization.enable = lib.mkDefault false; virtualization.host.bpmp.enable = lib.mkDefault false; passthroughs.host.uarta.enable = lib.mkDefault false; virtualization.host.gpio.enable = lib.mkDefault true; From aa7ec4f4e84be17c49436667548bdde6cea80d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Mon, 15 Apr 2024 07:03:39 +0000 Subject: [PATCH 08/26] add documentation for GPIO passthrough --- docs/src/technologies/nvidia_agx_pt_gpio.md | 414 ++++++++++++++++++ .../agx-gpiovm-passthrough.nix | 57 +++ .../gpio_pt_guest_overlay.dtso | 17 + .../microvm/virtualization/microvm/gpiovm.nix | 91 ++-- targets/nvidia-jetson-orin/flake-module.nix | 2 +- 5 files changed, 555 insertions(+), 26 deletions(-) create mode 100644 docs/src/technologies/nvidia_agx_pt_gpio.md create mode 100644 modules/jetpack-microvm/gpio_pt_guest_overlay.dtso diff --git a/docs/src/technologies/nvidia_agx_pt_gpio.md b/docs/src/technologies/nvidia_agx_pt_gpio.md new file mode 100644 index 000000000..95ce19c90 --- /dev/null +++ b/docs/src/technologies/nvidia_agx_pt_gpio.md @@ -0,0 +1,414 @@ + + +# NVIDIA Jetson AGX Orin: GPIO Passthrough + +This document describes the GPIO passthrough implementation on the NVIDIA Jetson AGX Orin board. The purpose of GPIO passthrough is to allow a virtual machine to access GPIO via the available GPIO chips. + +## GPIO Chips and Lines + +There are two GPIO chips in Nvidia Jetson AGX controlling GPIO, + +- _tegra234-gpio_, controlling 164 lines + - On /dev/gpiochip0, it is also refered to as "gpio_main" + - (See Appendix A.1) +- _tegra234-gpio-aon_, controlling 32 lines + - On /dev/gpiochip1, it is also refered to as "gpio_aon"and "gpio_main_aon" + - (See Appendix A.2) + +Each GPIO chip controll a number of GPIO lines. _tegra234-gpio_, controls 164 lines and _tegra234-gpio-aon_ controls 32 lines. Each line is a logical GPIO pin and the line number determines the offset from the GPIO chip's base. + +Many of the lines are by default reserved for built in functionality such as regulators, resets, interrupts and camera control (which is using lines for I2S, SPI and other functions) and internal ports such as UART (UARTA or UART1). Some ports are free to use without disrupting the normal operations of Jetson AGX. However reconfiguration of reserved functions is as such possible, but not recommended. +See Appendix A. + +Some lines are brought to the pinout of the Jetson 40-pin header. (See Appendix B) +Note that not all pins on the 40-pin header are controlled by the GPIO chips. Some pins have dedicated driver circuitry, thus not available for GPIO nor for GPIO passthrough. + +## GPIO Passthrough Host and Guest kernel modules + +A kernel driver in Host is acting as a proxy for the Guest VM to operate the GPIO chips and thereby the GPIO lines. This driver is implemented as a built-in kernel module. In Guest the tegra186-gpio kernel driver is hooked to send GPIO request to and receive replies from the /dev/vda device which passes the messages to host-passthrough and the Host's GPIO proxy driver. + + +## Host Device Tree + +To prepare GPIO on the host for the passthrough: + +>Add a virtual node to root in the Device Tree using an overlay file. + A driver associated to this node will find it using the compatible field. +``` + /plugin/; + + /{ + overlay-name = "GPIO passthrough on host"; + compatible = "nvidia,p3737-0000+p3701-0000\0nvidia,tegra234\0nvidia,tegra23x"; + + fragment@0 + { target-path = "/"; + __overlay__ + { gpio_host_proxy + { compatible = "nvidia,gpio-host-proxy"; + status = "okay"; + }; + }; + }; + }; +``` + + +## Guest Device Tree + + The Guest's Device tree serves two purposes. + - to define the _vda_ passthrough device + - to select which lines are allowed for passtrough + +### Creating the Guest Device Tree + +The Guest's Device Tree is based on the device tree extracted from QEMU VM. + +>To get the base QEMU device tree, run the following command: + +``` + qemu-system-aarch64 -machine virt,accel=kvm,dumpdtb=virt.dtb -cpu host +``` + + ### Apply an overlay to define the VDA device +The Device Tree defines passthrough memory for the /dev/vda passthrough device with the parameter _virtual-pa_. +>Add the passthrough device as a root node to the virtual machine's device tree: + +``` + /plugin/; + + /{ + overlay-name = "GPIO passthrough on host"; + compatible = "nvidia,p3737-0000+p3701-0000\0nvidia,tegra234\0nvidia,tegra23x"; + + fragment@0 + { target-path = "/"; + __overlay__ + { + gpio: gpio { + compatible = "nvidia,tegra234-bpmp"; + virtual-pa = <0x0 0x090c0000>; + status = "okay"; + } + }; + }; + }; +``` + +> The *gpio* node was added to the root node. + +### Apply an overlay to define allowed lines for passthrough + +The GPIO lines Selected for Passthrough are defined by the Guest's Device Tree + +>The Device Tree in the Guest virtual machine will determine which Host GPIO pins are visible and usable for the Guest. This is not the only function of the Guest's Device Tree, also see section Guest Device Tree. + +``` + Selecting pins is TODO -- put guest overlay here +``` + + +## Starting the Guest VM + +To start the guest VM: + +1. Set kernel startup paramters on host: +``` + iommu=pt vfio.enable_unsafe_noiommu_mode=0 vfio_iommu_type1.allow_unsafe_interrupts=1 vfio_platform.reset_required=0 +``` +2. Set kernel parameters for guest VM in microvm (or QEMU): + +``` + rootwait root=/dev/vda console=ttyAMA0 +``` +3. Set the device tree for guest according (see section Guest Device Tree) + +## Testing the passtrough + +For testing we need to make the Guest VM use GPIO ports. this can be done either via an UARTA VM console, provided by UARTA passthrough, or by letting the VM's systemd testing service use the ports. + +In both cases GPIO port functionality can be verified from the 40-pin header using a logic analysator connected to the 40-head port. (See Appendix C) + +### Using a VM console over UARTA + +If you use the built in systemd service in Guest to test the GPIO ports you do not need to follow the steps to enable UARTA. In earier versions of Tegra kernel code, patches for UARTA/BPMP and GPIO were conflicting. Also, it is not certain that that UARTA and GPIO passthrough is shared in the same virtual machine in future versions of Ghaf. + +If you are using UARTA as a debug port, stop the microvm@gpio-vm service if it is running. in the VM execute: +``` + sudo systemctl stop microvm@gpio-vm +``` + +1. Connect the NVIDIA Jetson AGX Orin Debug USB to your PC and open the serial port ttyACM1 at 115200 bps. You can use picocom with this command: + +``` + picocom -b 115200 /dev/ttyACM1 +``` +2. When the guest VM is launched you can see the VM Linux command line in the opened ttyACM1 terminal. +3. A script testing the ports vcan be executed or any other CLI commands that set up and use available ports. + +### Using the predefined systemd testing service + +A systemd service called microvm@gpio-vm is enabled in the Guest VM and it starts to execute a testing script. It is set to execute the _simple-chardev-test.sh_ bash script. + +To verify pin functionality. Connect a logic analyser to pins GPIO08, GPIO09, GPIO27, GPIO35 on the 40-pin Jetson header. (See appendix C.) + +## Appendixes + +### Appendix A.1 + +Line is the offset from each gpiochoip's base address. Direciton and comment declare defalult use. + +####gpiochip0 / tegra234-gpio - 164 lines +| gpio line | pin | default direction | comment | +| ---------|-------|--------|----------------------------------------------------| +| line 0 | PA.00 | output | consumer=fixed-regulators:regulator@111 | +| line 1 | PA.01 | output | active-low consumer=fixed-regulators:regulator@114 | +| line 2 | PA.02 | output | | +| line 3 | PA.03 | output | consumer=fixed-regulators:regulator@110 | +| line 4 | PA.04 | input | | +| line 5 | PA.05 | input | | +| line 6 | PA.06 | input | | +| line 7 | PA.07 | input | | +| line 8 | PB.00 | input | | +| line 9 | PC.00 | input | | +| line 10 | PC.01 | input | | +| line 11 | PC.02 | input | | +| line 12 | PC.03 | input | | +| line 13 | PC.04 | input | | +| line 14 | PC.05 | input | | +| line 15 | PC.06 | input | | +| line 16 | PC.07 | input | | +| line 17 | PD.00 | input | | +| line 18 | PD.01 | input | | +| line 19 | PD.02 | input | | +| line 20 | PD.03 | input | | +| line 21 | PE.00 | input | | +| line 22 | PE.01 | input | | +| line 23 | PE.02 | input | | +| line 24 | PE.03 | input | | +| line 25 | PE.04 | input | | +| line 26 | PE.05 | input | | +| line 27 | PE.06 | input | | +| line 28 | PE.07 | input | | +| line 29 | PF.00 | input | | +| line 30 | PF.01 | input | | +| line 31 | PF.02 | input | | +| line 32 | PF.03 | input | | +| line 33 | PF.04 | input | | +| line 34 | PF.05 | input | | +| line 35 | PG.00 | input | active-low consumer=force-recovery | +| line 36 | PG.01 | input | consumer=temp-alert | +| line 37 | PG.02 | input | active-low consumer=sleep | +| line 38 | PG.03 | output | | +| line 39 | PG.04 | input | | +| line 40 | PG.05 | input | | +| line 41 | PG.06 | input | | +| line 42 | PG.07 | input | consumer=cd | +| line 43 | PH.00 | input | GPIO35 | +| line 44 | PH.01 | output | | +| line 45 | PH.02 | input | | +| line 46 | PH.03 | output | consumer=camera-control-output-low | +| line 47 | PH.04 | output | consumer=fixed-regulators:regulator@105 | +| line 48 | PH.05 | output | | +| line 49 | PH.06 | output | consumer=camera-control-output-low | +| line 50 | PH.07 | input | I2S2_CLK | +| line 51 | PI.00 | input | I2S_SDIN | +| line 52 | PI.01 | input | I2S_SDOUT | +| line 53 | PI.02 | input | I2S_FS | +| line 54 | PI.03 | input | | +| line 55 | PI.04 | input | | +| line 56 | PI.05 | input | | +| line 57 | PI.06 | input | | +| line 58 | PJ.00 | input | | +| line 59 | PJ.01 | input | | +| line 60 | PJ.02 | input | | +| line 61 | PJ.03 | input | | +| line 62 | PJ.04 | input | | +| line 63 | PJ.05 | input | | +| line 64 | PK.00 | input | | +| line 65 | PK.01 | input | | +| line 66 | PK.02 | input | | +| line 67 | PK.03 | input | | +| line 68 | PK.04 | input | | +| line 69 | PK.05 | output | | +| line 70 | PK.06 | input | | +| line 71 | PK.07 | input | | +| line 72 | PL.00 | input | | +| line 73 | PL.01 | input | | +| line 74 | PL.02 | input | | +| line 75 | PL.03 | input | | +| line 76 | PM.00 | input | | +| line 77 | PM.01 | input | | +| line 78 | PM.02 | input | | +| line 79 | PM.03 | input | | +| line 80 | PM.04 | input | | +| line 81 | PM.05 | input | | +| line 82 | PM.06 | input | | +| line 83 | PM.07 | input | | +| line 84 | PN.00 | input | | +| line 85 | PN.01 | input | GPIO27 | +| line 86 | PN.02 | input | | +| line 87 | PN.03 | output | | +| line 88 | PN.04 | input | | +| line 89 | PN.05 | input | | +| line 90 | PN.06 | input | | +| line 91 | PN.07 | input | | +| line 92 | PP.00 | input | | +| line 93 | PP.01 | input | | +| line 94 | PP.02 | input | | +| line 95 | PP.03 | input | | +| line 96 | PP.04 | input | GPIO17 | +| line 97 | PP.05 | input | | +| line 98 | PP.06 | input | | +| line 99 | PP.07 | input | | +| line 100 | PQ.00 | input | | +| line 101 | PQ.01 | output | consumer=fixed-regulators:regulator@106 | +| line 102 | PQ.02 | input | | +| line 103 | PQ.03 | input | | +| line 104 | PQ.04 | output | | +| line 105 | PQ.05 | input | | +| line 106 | PQ.06 | input | MCLK05 | +| line 107 | PQ.07 | input | | +| line 108 | PR.00 | input | GPIO32 | +| line 109 | PR.01 | input | | +| line 110 | PR.02 | input | | +| line 111 | PR.03 | input | | +| line 112 | PR.04 | input | UART1_RTS | +| line 113 | PR.05 | input | UART1_CTS | +| line 114 | PX.00 | input | | +| line 115 | PX.01 | input | | +| line 116 | PX.02 | input | | +| line 117 | PX.03 | input | | +| line 118 | PX.04 | input | | +| line 119 | PX.05 | input | | +| line 120 | PX.06 | input | | +| line 121 | PX.07 | input | | +| line 122 | PY.00 | output | | +| line 123 | PY.01 | output | consumer=phy_reset | +| line 124 | PY.02 | output | | +| line 125 | PY.03 | input | consumer=interrupt | +| line 126 | PY.04 | input | consumer=interrupt | +| line 127 | PY.05 | input | | | +| line 128 | PY.06 | input | | +| line 129 | PY.07 | input | | +| line 130 | PZ.00 | output | | +| line 131 | PZ.01 | input | | +| line 132 | PZ.02 | input | | +| line 133 | PZ.03 | input | SPI1_MOSI_SCK | +| line 134 | PZ.04 | input | SPI1_MOSI | +| line 135 | PZ.05 | input | SPI1_MOSI | +| line 136 | PZ.06 | input | SPI1_CS0_N | +| line 137 | PZ.07 | input | SPI1_CS1_N | +| line 138 | PAC.00 | output | consumer=camera-control-output-low | +| line 139 | PAC.01 | output | consumer=camera-control-output-low | +| line 140 | PAC.02 | output | | +| line 141 | PAC.03 | input | | +| line 142 | PAC.04 | input | | +| line 143 | PAC.05 | input | consumer=interrupt | +| line 144 | PAC.06 | input | | +| line 145 | PAC.07 | output | consumer=fixed-regulators:regulator@115 | +| line 146 | PAD.00 | input | | +| line 147 | PAD.01 | input | | +| line 148 | PAD.02 | input | | +| line 149 | PAD.03 | input | | +| line 150 | PAE.00 | input | | +| line 151 | PAE.01 | input | | +| line 152 | PAF.00 | input | | +| line 153 | PAF.01 | input | | +| line 154 | PAF.02 | input | | +| line 155 | PAF.03 | input | | +| line 156 | PAG.00 | input | | +| line 157 | PAG.01 | input | | +| line 158 | PAG.02 | input | | +| line 159 | PAG.03 | input | | +| line 160 | PAG.04 | input | | +| line 161 | PAG.05 | input | | +| line 162 | PAG.06 | input | | +| line 163 | PAG.07 | input | | + +### Appendix A.2 + +Line is the offset from each gpiochoip's base address. Direciton and comment declare defalult use. + +####gpiochip1 / tegra234-gpio-aon - 32 lines: +| gpio line | pin | default direction | comment | +| ---------|-------|--------|----------------------------------------------------| +| line 0 | PAA.00 | input | | +| line 1 | PAA.01 | input | | +| line 2 | PAA.02 | input | | +| line 3 | PAA.03 | input | | +| line 4 | PAA.04 | input | | +| line 5 | PAA.05 | input | | +| line 6 | PAA.06 | input | | +| line 7 | PAA.07 | input | | +| line 8 | PBB.00 | input | GPIO9 | +| line 9 | PBB.01 | input | GPIO8 | +| line 10 | PBB.02 | input | | +| line 11 | PBB.03 | output | | +| line 12 | PCC.00 | input | | +| line 13 | PCC.01 | input | | +| line 14 | PCC.02 | output | consumer=fixed-regulators:regulator@116 | +| line 15 | PCC.03 | input | | +| line 16 | PCC.04 | input | | +| line 17 | PCC.05 | input | | +| line 18 | PCC.06 | input | | +| line 19 | PCC.07 | input | | +| line 20 | PDD.00 | input | | +| line 21 | PDD.01 | input | | +| line 22 | PDD.02 | input | | +| line 23 | PEE.00 | input | | +| line 24 | PEE.01 | input | | +| line 25 | PEE.02 | input | | +| line 26 | PEE.03 | input | | +| line 27 | PEE.04 | input | active-low consumer=power-key | +| line 28 | PEE.05 | input | | +| line 29 | PEE.06 | input | | +| line 30 | PEE.07 | input | | +| line 31 | PGG.00 | input | | + +### Appendix B + +#### Jetson AGX Orin J30 GPIO Expansion Header pinout + +|Line|Sysfs GPIO| Connector Label | Description | More | Pin || Pin | Connector Label | Description | More |Line|Sysfs GPIO| +|----|----------|-----------------|--------------|---------------|------||------|--------------------|---------------|--------|----|----------| +| | | 3.3 VDC | Power | 1A max | **1**|| **2**| 5.0 VDC | Power | 1A max | | | +| | |I2C5_DAT|General I2C5 Data| 1.8/3.3V, I2C Bus 8 | **3**|| **4**| 5.0 VDC | Power | 1A max | | | +| | | 2C_GP5_CLK | General I2C #5 Clock | 1.8/3.3V, I2C Bus 8 | **5**|| **6**| GND | | | | | +|106 | gpio454 | MCLK05 | Audio Master Clock | 1.8/3.3V | **7**|| **8**| UART1_TX | UART #1 |Transmit| | | +| | | GND | | |**9 **||**10**| UART1_RX | UART #1 |Receive | | | +|112 | gpio460 | UART1_RTS | UART #1 Request to Send |1.8/3.3V |**11**||**12**| I2S2_CLK | Audio I2S #2 | Clock | 50 | gpio398 | +|108 | gpio456 | **GPIO32** | GPIO #32 | |**13**||**14**| GND | | | +|85 | gpio433 | **GPIO27** | (PWM) | |**15**||**16**| **GPIO8** | GPIO #8 | | 9 | gpio357 | +| | | 3.3 VDC | Power | 1A max |**17**||**18**| **GPIO35** | (PWM) | | 43 | gpio391 | +|135 | gpio483 | SPI1_MOSI | SPI #1 | Master Out/Slave In |**19**||**20**| GND | | | | | +|134 | gpio482 | SPI1_MOSI | SPI #1 | Master In/Slave Out |**21**||**22**| GPIO17 | GPIO #17 | | 96 | gpio444 | +|133 | gpio481 | SPI1_SCK | SPI #1 | Shift Clock |**23**||**24**| SPI1_CS0_N | SPI #1 |Chip Select #0 | 136| gpio484 | +| | | GND | | |**25**||**26**| SPI1_CS1_N | SPI #1 |Chip Select #1 | 137| gpio485 | +| | | I2C2_DAT | General I2C #2 Data | I2C Bus 1 |**27**||**28**| I2C2_CLK | General I2C #2 Clock | I2C Bus 1 | | | +|1 | gpio317 | CAN0_DIN |CAN #0 | Data In |**29**||**30**| GND | | | | | +|0 | gpio316 | CAN0_DOUT |CAN #0 | Data Out |**31**||**32**| **GPIO9** | GPIO #9 | | 8 | gpio324 | +|2 | gpio318 | CAN1_DOUT |CAN #1 | Data Out |**33**||**34**| GND | | | | | +|53 | gpio401 | I2S_FS | AUDIO I2S #2 Left/Right | Clock |**35**||**36**| UART1_CTS |UART #1 | Clear to Send | 113| gpio461 | +|3 | gpio319 | CAN1_DIN | CAN #1 Data In | |**37**||**38**| I2S_SDIN | Audio I2S #2 |Data In | 52 | gpio400 | +| | | GND | | |**39**||**40**| I2S_SDOUT | Audio I2S #2 |Data Out| 51 | gpio399 | + +On I2C bus 1, there are existing devices on 0x08, 0x40, 0x41. These are denoted as UU by i2cdetect +Default Setup + +The initial pinmux should set all of the these pins, except for the power, UART RX TX and two I2C busses, to GPIO at boot. +Usage designations + +The usages described in the above table is the official NVIDIA suggested pin usage for SFIO functionality. A modified device tree or modification to the appropriate registers is required before using as the described function. +Base GPIO Addresses + + - There are two GPIO bases for the line offset, 316 and 348. + - First number is the GPIO number (i.e. the line offset) within a GPIO controller , compare to Appendix A.1 and A.2 + - Second ( Sysfs GPIO / gpioXXXX ) is the global Linux GPIO number + - Pin 15 – When configured as PWM: + - PWM chip sysfs directory: /sys/devices/3280000.pwm + - Pin 18 – When configured as PWM: + - PWM chip sysfs directory: /sys/devices/32c0000.pwm \ No newline at end of file diff --git a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix index 8544a538a..39dcf1d90 100644 --- a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix +++ b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix @@ -5,6 +5,50 @@ config, ... }: let + # bugtest variable + # absoluteFilePath = "${builtins.currentSystem}/source/nixos-modules/host/"; + # absoluteFilePath = getBuildDir; + + # guestdts specifies specifically gpiovm's device tree + gpioGuestPath = "./arch/arm64/boot/dts/nvidia/"; + gpioGuestSrcName = "tegra234-p3701-0000-p3737-0000.dtb"; + gpioGuestSrc = gpioGuestPath + gpioGuestSrcName; + # gpioGuestDtbName = "tegra234-p3701-0000-gpio-passthrough.dtb"; + # gpioGuestDtb = gpioGuestPath + gpioGuestDtbName; + # tmp debug fix -- fix bypasses copy which is a bug + gpioGuestDtb = gpioGuestSrc; # this line bypasses copy of DT blob -- for debug reasons + gpioGuestDtbName = gpioGuestSrcName; + + # TODO we do not have a proper ./gpio_pt_guest_overlay.dtso yet -- using host's for build debugging + gpioGuestDtso = ./gpio_pt_host_overlay.dtso; + + /* + pkgs.stdenv.mkDerivation { + inherit gpioGuestSrc gpioGuestDtb; # Ensure these variables are available in the builder script + name = "copy-dtb"; + buildCommand = pkgs.writeText "copy-dtb.sh" '' + cp ${gpioGuestSrc} ${gpioGuestDtb} + ''; + } + */ + + # runCommand "copy-dtb" {} "coreutils-full./bin/cp gpioGuestSrc gpioGuestDtb" + + /* + pkgs.runCommand "copy-dtb" {} '' + cp ${gpioGuestSrc} ${gpioGuestDtb} + ''; + */ + + /* + # Creating a new DTB file + pkgs.buildPackages.utils.copyFile { + inputFile = gpioGuestSrc; + outputFile = gpioGuestDtb; + override = true; + }; + */ + cfg = config.ghaf.hardware.nvidia.orin.agx; in { options.ghaf.hardware.nvidia.orin.agx.enableGPIOPassthrough = @@ -29,6 +73,19 @@ in { # "root=/dev/vda" "console=ttyAMA0" ]; + + hardware.deviceTree = { + enable = true; + name = gpioGuestDtbName; + overlays = [ + { + name = "gpio_pt_guest_overlay"; + # TODO we do not have ./gpio_pt_guest_overlay.dtso yet + dtsFile = gpioGuestDtso; + filter = gpioGuestDtbName; + } + ]; + }; } ]; diff --git a/modules/jetpack-microvm/gpio_pt_guest_overlay.dtso b/modules/jetpack-microvm/gpio_pt_guest_overlay.dtso new file mode 100644 index 000000000..47bbb567e --- /dev/null +++ b/modules/jetpack-microvm/gpio_pt_guest_overlay.dtso @@ -0,0 +1,17 @@ +/dts-v1/; +/plugin/; + +/{ + overlay-name = "GPIO passthrough on host"; + compatible = "nvidia,p3737-0000+p3701-0000\0nvidia,tegra234\0nvidia,tegra23x"; + + fragment@0 + { target-path = "/"; + __overlay__ + { gpio_host_proxy + { compatible = "nvidia,gpio-host-proxy"; + status = "okay"; + }; + }; + }; +}; diff --git a/modules/microvm/virtualization/microvm/gpiovm.nix b/modules/microvm/virtualization/microvm/gpiovm.nix index b0e9bec63..0d545b20b 100644 --- a/modules/microvm/virtualization/microvm/gpiovm.nix +++ b/modules/microvm/virtualization/microvm/gpiovm.nix @@ -10,19 +10,45 @@ vmName = "gpio-vm"; /* + # bugtest variable + # absoluteFilePath = "${builtins.currentSystem}/source/nixos-modules/host/"; + # absoluteFilePath = getBuildDir; + # guestdts specifies specifically gpiovm's device tree - # todo synchronise with (move to) definition in ../host/gpio-virt-host/default.nix - dtbSrc = "tegra234-p3701-0000-p3737-0000"; - dtName = "tegra234-p3701-gpio-guestvm"; + gpioGuestSrcName = "tegra234-p3701-0000-p3737-0000.dtb"; + gpioGuestDtbName = "tegra234-p3701-0000-gpio-passthrough.dtb"; + gpioGuestPath = "./arch/arm64/boot/dts/nvidia/"; + gpioGuestSrc = gpioGuestPath + gpioGuestSrcName; + # gpioGuestDtb = gpioGuestPath + gpioGuestDtbName; + # tmp debug fix + gpioGuestDtb = gpioGuestSrc; # this line bypasses copy of DT blob -- for debug reasons + # TODO we do not have ./gpio_pt_guest_overlay.dtso yet - dtsFile = ../../..jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/gpio_pt_host_overlay.dtso; - gpioGuestOutput = dtName + ".dtb"; - gpioGuestDtb = new File gpioPtGuestOutput {}; - - # Creating a new DTB file named gpio_pt_guest.dtb - nixpkgs.buildInputs.utils.copy-file { - inputFile = "${sourcePath}/${name}"; - outputFile = gpioPtGuestOutput; + dtsoGpioFile = "./gpio_pt_host_overlay.dtso"; + */ + /* + pkgs.stdenv.mkDerivation { + inherit gpioGuestSrc gpioGuestDtb; # Ensure these variables are available in the builder script + name = "copy-dtb"; + buildCommand = pkgs.writeText "copy-dtb.sh" '' + cp ${gpioGuestSrc} ${gpioGuestDtb} + ''; + } + */ + + # runCommand "copy-dtb" {} "coreutils-full./bin/cp gpioGuestSrc gpioGuestDtb" + + /* + pkgs.runCommand "copy-dtb" {} '' + cp ${gpioGuestSrc} ${gpioGuestDtb} + ''; + */ + + /* + # Creating a new DTB file + pkgs.buildPackages.utils.copyFile { + inputFile = gpioGuestSrc; + outputFile = gpioGuestDtb; override = true; }; */ @@ -57,20 +83,6 @@ # we define a servce in extraModules variable below with import ./gpio-test.nix } */ - /* - options.hardware.devicetree = { - enable = true; - name = dtName; - overlays = [ - { - name = "gpio_pt_guest_overlay"; - # TODO we do not have ./gpio_pt_guest_overlay.dtso yet - dtsFile = dtsFile; - filter = gpioPtGuestOutput; - } - ]; - }; - */ microvm = { optimize.enable = true; shares = [ @@ -108,6 +120,9 @@ in { }; }; + #pkgs.runCommand "copy-dtb" {} "pkgs.coreutils-full./bin/cp gpioGuestSrc gpioGuestDtb" + # runCommand "copy-dtb" {} "coreutils-full./bin/cp gpioGuestSrc gpioGuestDtb" + config = lib.mkIf cfg.enable { microvm.vms."${vmName}" = { autostart = true; @@ -117,8 +132,34 @@ in { imports = gpiovmBaseConfiguration.imports ++ cfg.extraModules; + /* + hardware.deviceTree = { + enable = true; + name = gpioGuestDstName; + overlays = [ + { + name = "gpio_pt_guest_overlay"; + # TODO we do not have ./gpio_pt_guest_overlay.dtso yet + # dtsFile = builtins.toPath gpioGuestDtb; + filter = gpioGuestDtbName; + } + ]; + }; + */ }; # specialArgs = {inherit lib;}; + + }; + + /* + # Creating a new DTB file + # pkgs.buildPackages.utils.copyFile { + # pkgs.stdenv.lib.callPackage { + pkgs.stdenv.lib.copyFile { + inputFile = gpioGuestSrc; + outputFile = gpioGuestDtb; + override = true; }; + */ }; } diff --git a/targets/nvidia-jetson-orin/flake-module.nix b/targets/nvidia-jetson-orin/flake-module.nix index 5b3c201d2..532926038 100644 --- a/targets/nvidia-jetson-orin/flake-module.nix +++ b/targets/nvidia-jetson-orin/flake-module.nix @@ -63,7 +63,7 @@ }; hardware.nvidia = { - # virtualization.enable = lib.mkDefault true; + virtualization.enable = lib.mkDefault false; virtualization.host.bpmp.enable = lib.mkDefault false; passthroughs.host.uarta.enable = lib.mkDefault false; virtualization.host.gpio.enable = lib.mkDefault true; From 739ec6d8110bf02cc6f3e4eae423b25db803d967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Mon, 15 Apr 2024 15:00:52 +0000 Subject: [PATCH 09/26] Towards a microvm@gpio-vm with its own Device Tree --- modules/jetpack-microvm/agx-gpiovm-passthrough.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix index 39dcf1d90..8baec834b 100644 --- a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix +++ b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix @@ -20,7 +20,7 @@ gpioGuestDtbName = gpioGuestSrcName; # TODO we do not have a proper ./gpio_pt_guest_overlay.dtso yet -- using host's for build debugging - gpioGuestDtso = ./gpio_pt_host_overlay.dtso; + gpioGuestDtso = ./gpio_pt_guest_overlay.dtso; /* pkgs.stdenv.mkDerivation { From 7e477ebbbd1d88b6d2cd9e2db526a2808c4958bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Tue, 16 Apr 2024 13:39:16 +0000 Subject: [PATCH 10/26] fails to build the right device tree for microvm@gpio-vm startup of VM fails because the DT does not provide the vda device --- docs/src/technologies/nvidia_agx_pt_gpio.md | 24 +++---- .../agx-gpiovm-passthrough.nix | 62 +++++++++++++------ .../gpio_pt_guest_overlay.dtso | 16 ++--- 3 files changed, 66 insertions(+), 36 deletions(-) diff --git a/docs/src/technologies/nvidia_agx_pt_gpio.md b/docs/src/technologies/nvidia_agx_pt_gpio.md index 95ce19c90..5f25521f9 100644 --- a/docs/src/technologies/nvidia_agx_pt_gpio.md +++ b/docs/src/technologies/nvidia_agx_pt_gpio.md @@ -38,14 +38,15 @@ To prepare GPIO on the host for the passthrough: >Add a virtual node to root in the Device Tree using an overlay file. A driver associated to this node will find it using the compatible field. ``` + /dts-v1/; /plugin/; /{ - overlay-name = "GPIO passthrough on host"; - compatible = "nvidia,p3737-0000+p3701-0000\0nvidia,tegra234\0nvidia,tegra23x"; + overlay-name = "GPIO passthrough on host"; + compatible = "nvidia,p3737-0000+p3701-0000\0nvidia,tegra234\0nvidia,tegra23x"; - fragment@0 - { target-path = "/"; + fragment@0 + { target-path = "/"; __overlay__ { gpio_host_proxy { compatible = "nvidia,gpio-host-proxy"; @@ -78,23 +79,24 @@ The Device Tree defines passthrough memory for the /dev/vda passthrough device w >Add the passthrough device as a root node to the virtual machine's device tree: ``` + /dts-v1/; /plugin/; /{ - overlay-name = "GPIO passthrough on host"; - compatible = "nvidia,p3737-0000+p3701-0000\0nvidia,tegra234\0nvidia,tegra23x"; + overlay-name = "GPIO passthrough on guest"; + compatible = "nvidia,p3737-0000+p3701-0000\0nvidia,tegra234\0nvidia,tegra23x"; - fragment@0 - { target-path = "/"; + fragment@0 + { target-path = "/"; __overlay__ { gpio: gpio { compatible = "nvidia,tegra234-bpmp"; virtual-pa = <0x0 0x090c0000>; status = "okay"; - } + }; }; - }; + }; }; ``` @@ -411,4 +413,4 @@ Base GPIO Addresses - Pin 15 – When configured as PWM: - PWM chip sysfs directory: /sys/devices/3280000.pwm - Pin 18 – When configured as PWM: - - PWM chip sysfs directory: /sys/devices/32c0000.pwm \ No newline at end of file + - PWM chip sysfs directory: /sys/devices/32c0000.pwm diff --git a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix index 8baec834b..e1a9e8c0e 100644 --- a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix +++ b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix @@ -1,49 +1,58 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ +{ + pkgs, lib, config, ... }: let - # bugtest variable - # absoluteFilePath = "${builtins.currentSystem}/source/nixos-modules/host/"; - # absoluteFilePath = getBuildDir; + #kernel = options.ghaf.host.kernel; + kernel = config.ghaf.host.kernel; # guestdts specifies specifically gpiovm's device tree - gpioGuestPath = "./arch/arm64/boot/dts/nvidia/"; - gpioGuestSrcName = "tegra234-p3701-0000-p3737-0000.dtb"; - gpioGuestSrc = gpioGuestPath + gpioGuestSrcName; + gpioGuestPath = ./arch/arm64/boot/dts/nvidia; + gpioGuestOrigName = ''tegra234-p3701-0000-p3737-0000.dtb''; + gpioGuestOrig = gpioGuestPath + gpioGuestOrigName; # gpioGuestDtbName = "tegra234-p3701-0000-gpio-passthrough.dtb"; # gpioGuestDtb = gpioGuestPath + gpioGuestDtbName; + + gpioGuestDts = ./gpio_pt_guest_overlay.dtso; + # tmp debug fix -- fix bypasses copy which is a bug - gpioGuestDtb = gpioGuestSrc; # this line bypasses copy of DT blob -- for debug reasons - gpioGuestDtbName = gpioGuestSrcName; + gpioGuestDtb = gpioGuestOrig; # this line bypasses copy of DT blob -- for debug reasons + gpioGuestDtbName = gpioGuestOrigName; + + /* + gpioGuestCopyDtb = runCommand "copy dtb file for guest" {} + '' + cp ${gpioGuestOrig} ${gpioGuestDtb} + ''; + */ # TODO we do not have a proper ./gpio_pt_guest_overlay.dtso yet -- using host's for build debugging - gpioGuestDtso = ./gpio_pt_guest_overlay.dtso; /* pkgs.stdenv.mkDerivation { - inherit gpioGuestSrc gpioGuestDtb; # Ensure these variables are available in the builder script + inherit gpioGuestOrig gpioGuestDtb; # Ensure these variables are available in the builder script name = "copy-dtb"; buildCommand = pkgs.writeText "copy-dtb.sh" '' - cp ${gpioGuestSrc} ${gpioGuestDtb} + cp ${gpioGuestOrig} ${gpioGuestDtb} ''; } */ - # runCommand "copy-dtb" {} "coreutils-full./bin/cp gpioGuestSrc gpioGuestDtb" + # pkgs.runCommand "copy-dtb" {} "coreutils-full./bin/cp gpioGuestOrig gpioGuestDtb" /* pkgs.runCommand "copy-dtb" {} '' - cp ${gpioGuestSrc} ${gpioGuestDtb} + cp ${gpioGuestOrig} ${gpioGuestDtb} ''; */ /* # Creating a new DTB file pkgs.buildPackages.utils.copyFile { - inputFile = gpioGuestSrc; + inputFile = gpioGuestOrig; outputFile = gpioGuestDtb; override = true; }; @@ -54,9 +63,9 @@ in { options.ghaf.hardware.nvidia.orin.agx.enableGPIOPassthrough = lib.mkEnableOption "GPIO passthrough to VM"; + config = lib.mkIf cfg.enableGPIOPassthrough { # Orin AGX GPIO Passthrough - # Debug statement to log a message ghaf.virtualization.microvm.gpiovm.extraModules = [ { @@ -70,7 +79,7 @@ in { microvm.kernelParams = [ "rootwait" - # "root=/dev/vda" + "root=/dev/vda" "console=ttyAMA0" ]; @@ -81,13 +90,30 @@ in { { name = "gpio_pt_guest_overlay"; # TODO we do not have ./gpio_pt_guest_overlay.dtso yet - dtsFile = gpioGuestDtso; + dtsFile = gpioGuestDts; filter = gpioGuestDtbName; } ]; }; + /* + buildPhase = '' + # Copy the dtb file + # TODO: Adjust the command to copy the dtb file as needed + # cp ${gpioGuestOrig} ${gpioGuestDtb} + ''; + installPhase = '' + pwd + ''; + */ } ]; + + /* + kernel = { + inherit kernel; + #phases = _ old.phases + { name='install'; func = ''echo "do copy here"'' }; + }; + */ /* tmp note: further kernel settings for nvidia in: ../jetpack/nvidia-jetson-orin/virtualization/default.nix diff --git a/modules/jetpack-microvm/gpio_pt_guest_overlay.dtso b/modules/jetpack-microvm/gpio_pt_guest_overlay.dtso index 47bbb567e..2e7d1e9dc 100644 --- a/modules/jetpack-microvm/gpio_pt_guest_overlay.dtso +++ b/modules/jetpack-microvm/gpio_pt_guest_overlay.dtso @@ -2,15 +2,17 @@ /plugin/; /{ - overlay-name = "GPIO passthrough on host"; + overlay-name = "GPIO passthrough on guest"; compatible = "nvidia,p3737-0000+p3701-0000\0nvidia,tegra234\0nvidia,tegra23x"; - fragment@0 - { target-path = "/"; - __overlay__ - { gpio_host_proxy - { compatible = "nvidia,gpio-host-proxy"; - status = "okay"; + fragment@0 + { target-path = "/"; + __overlay__ + { + gpio: gpio { + compatible = "nvidia,tegra234-bpmp"; + virtual-pa = <0x0 0x090c0000>; + status = "okay"; }; }; }; From ea5f76b2c3ed14119859fcd1d39e9fcecda45ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Thu, 18 Apr 2024 09:02:02 +0000 Subject: [PATCH 11/26] gpio-vm starts up with an unique Device Tree gpio-vm's device tree tegra234-p3701-0000-gpio-passthrough.dtb declares the vda device needed for passthrough. gpio-vm goes into emergency mode because it cannot open AMA0 as a console. Uarta passthrough is disabled because of conflicts with GPIO passthrough. We need to disable the QEMU console. Somewhere in the code console=ttyAMA0 is set for QEMU. It is not obvious where. --- .../agx-gpiovm-passthrough.nix | 72 ++++-------------- .../host/gpio-virt-host/default.nix | 3 +- .../microvm/virtualization/microvm/gpiovm.nix | 73 ------------------- 3 files changed, 14 insertions(+), 134 deletions(-) diff --git a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix index e1a9e8c0e..34d6611f8 100644 --- a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix +++ b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix @@ -6,57 +6,22 @@ config, ... }: let - #kernel = options.ghaf.host.kernel; - kernel = config.ghaf.host.kernel; - - # guestdts specifies specifically gpiovm's device tree - gpioGuestPath = ./arch/arm64/boot/dts/nvidia; + # gpioDtbPath = ./arch/arm64/boot/dts/nvidia; gpioGuestOrigName = ''tegra234-p3701-0000-p3737-0000.dtb''; - gpioGuestOrig = gpioGuestPath + gpioGuestOrigName; - # gpioGuestDtbName = "tegra234-p3701-0000-gpio-passthrough.dtb"; - # gpioGuestDtb = gpioGuestPath + gpioGuestDtbName; + gpioGuestDtbName = "tegra234-p3701-0000-gpio-passthrough.dtb"; gpioGuestDts = ./gpio_pt_guest_overlay.dtso; - # tmp debug fix -- fix bypasses copy which is a bug - gpioGuestDtb = gpioGuestOrig; # this line bypasses copy of DT blob -- for debug reasons - gpioGuestDtbName = gpioGuestOrigName; - - /* - gpioGuestCopyDtb = runCommand "copy dtb file for guest" {} - '' - cp ${gpioGuestOrig} ${gpioGuestDtb} - ''; - */ - - # TODO we do not have a proper ./gpio_pt_guest_overlay.dtso yet -- using host's for build debugging - - /* - pkgs.stdenv.mkDerivation { - inherit gpioGuestOrig gpioGuestDtb; # Ensure these variables are available in the builder script - name = "copy-dtb"; - buildCommand = pkgs.writeText "copy-dtb.sh" '' - cp ${gpioGuestOrig} ${gpioGuestDtb} - ''; - } - */ - - # pkgs.runCommand "copy-dtb" {} "coreutils-full./bin/cp gpioGuestOrig gpioGuestDtb" - - /* - pkgs.runCommand "copy-dtb" {} '' - cp ${gpioGuestOrig} ${gpioGuestDtb} - ''; - */ - - /* - # Creating a new DTB file - pkgs.buildPackages.utils.copyFile { - inputFile = gpioGuestOrig; - outputFile = gpioGuestDtb; - override = true; + copyDtb = pkgs.stdenv.mkDerivation { + name = "copy-dtb-file"; + buildInputs = [ pkgs.coreutils-full.cp gpioGuestOrigName gpioGuestDtbName ]; + buildPhase = ''cp ${gpioGuestOrigName} ${gpioGuestDtbName}''; + outputs = [ gpioGuestDtbName ]; }; - */ + + # dtbFile specifies specifically gpiovm's device tree + dtbFileList = copyDtb.outputs; + dtbFile = builtins.elemAt dtbFileList 0; cfg = config.ghaf.hardware.nvidia.orin.agx; in { @@ -80,8 +45,8 @@ in { microvm.kernelParams = [ "rootwait" "root=/dev/vda" - "console=ttyAMA0" ]; + # "console=ttyAMA0" # removed gpio-vm cannot open console since it does not have uarta passthough hardware.deviceTree = { enable = true; @@ -89,22 +54,11 @@ in { overlays = [ { name = "gpio_pt_guest_overlay"; - # TODO we do not have ./gpio_pt_guest_overlay.dtso yet dtsFile = gpioGuestDts; - filter = gpioGuestDtbName; + filter = dtbFile; } ]; }; - /* - buildPhase = '' - # Copy the dtb file - # TODO: Adjust the command to copy the dtb file as needed - # cp ${gpioGuestOrig} ${gpioGuestDtb} - ''; - installPhase = '' - pwd - ''; - */ } ]; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix index 0f1464fae..74d2b3e7c 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix @@ -39,8 +39,7 @@ in { hardware.deviceTree = { # Enable hardware.deviceTree for handle host dtb overlays enable = true; - name = "tegra234-p3701-0000-p3737-0000"; - # name = "tegra234-p3701-0000-p3737-0000.dtb"; + name = "tegra234-p3701-0000-p3737-0000.dtb"; # name = "tegra234-p3701-host-passthrough.dtb"; # using overlay file: diff --git a/modules/microvm/virtualization/microvm/gpiovm.nix b/modules/microvm/virtualization/microvm/gpiovm.nix index 0d545b20b..5dcbe852b 100644 --- a/modules/microvm/virtualization/microvm/gpiovm.nix +++ b/modules/microvm/virtualization/microvm/gpiovm.nix @@ -9,50 +9,6 @@ configHost = config; vmName = "gpio-vm"; - /* - # bugtest variable - # absoluteFilePath = "${builtins.currentSystem}/source/nixos-modules/host/"; - # absoluteFilePath = getBuildDir; - - # guestdts specifies specifically gpiovm's device tree - gpioGuestSrcName = "tegra234-p3701-0000-p3737-0000.dtb"; - gpioGuestDtbName = "tegra234-p3701-0000-gpio-passthrough.dtb"; - gpioGuestPath = "./arch/arm64/boot/dts/nvidia/"; - gpioGuestSrc = gpioGuestPath + gpioGuestSrcName; - # gpioGuestDtb = gpioGuestPath + gpioGuestDtbName; - # tmp debug fix - gpioGuestDtb = gpioGuestSrc; # this line bypasses copy of DT blob -- for debug reasons - - # TODO we do not have ./gpio_pt_guest_overlay.dtso yet - dtsoGpioFile = "./gpio_pt_host_overlay.dtso"; - */ - /* - pkgs.stdenv.mkDerivation { - inherit gpioGuestSrc gpioGuestDtb; # Ensure these variables are available in the builder script - name = "copy-dtb"; - buildCommand = pkgs.writeText "copy-dtb.sh" '' - cp ${gpioGuestSrc} ${gpioGuestDtb} - ''; - } - */ - - # runCommand "copy-dtb" {} "coreutils-full./bin/cp gpioGuestSrc gpioGuestDtb" - - /* - pkgs.runCommand "copy-dtb" {} '' - cp ${gpioGuestSrc} ${gpioGuestDtb} - ''; - */ - - /* - # Creating a new DTB file - pkgs.buildPackages.utils.copyFile { - inputFile = gpioGuestSrc; - outputFile = gpioGuestDtb; - override = true; - }; - */ - gpiovmBaseConfiguration = { imports = [ ({lib, ...}: { @@ -120,9 +76,6 @@ in { }; }; - #pkgs.runCommand "copy-dtb" {} "pkgs.coreutils-full./bin/cp gpioGuestSrc gpioGuestDtb" - # runCommand "copy-dtb" {} "coreutils-full./bin/cp gpioGuestSrc gpioGuestDtb" - config = lib.mkIf cfg.enable { microvm.vms."${vmName}" = { autostart = true; @@ -132,34 +85,8 @@ in { imports = gpiovmBaseConfiguration.imports ++ cfg.extraModules; - /* - hardware.deviceTree = { - enable = true; - name = gpioGuestDstName; - overlays = [ - { - name = "gpio_pt_guest_overlay"; - # TODO we do not have ./gpio_pt_guest_overlay.dtso yet - # dtsFile = builtins.toPath gpioGuestDtb; - filter = gpioGuestDtbName; - } - ]; - }; - */ }; # specialArgs = {inherit lib;}; - - }; - - /* - # Creating a new DTB file - # pkgs.buildPackages.utils.copyFile { - # pkgs.stdenv.lib.callPackage { - pkgs.stdenv.lib.copyFile { - inputFile = gpioGuestSrc; - outputFile = gpioGuestDtb; - override = true; }; - */ }; } From 50c3e840fae4d516ee7e3a4e4029f7d6a2804a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Fri, 19 Apr 2024 08:57:11 +0000 Subject: [PATCH 12/26] Fixing overlays in gpio-vm At the moment device tree overlays are not working when applied according to template from other VM's. Posible cause is big changes introduced in upstream. Will merge with tiiuae after this commit. --- .../agx-gpiovm-passthrough.nix | 51 +++++++++++-------- .../common/bpmp-virt-common/default.nix | 3 ++ .../patches/0004-bpmp-virt-drivers.patch | 25 ++++----- .../patches/0004-gpio-virt-drivers.patch | 10 +++- targets/nvidia-jetson-orin/flake-module.nix | 8 +-- 5 files changed, 57 insertions(+), 40 deletions(-) diff --git a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix index 34d6611f8..137dcedab 100644 --- a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix +++ b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix @@ -8,16 +8,18 @@ }: let # gpioDtbPath = ./arch/arm64/boot/dts/nvidia; gpioGuestOrigName = ''tegra234-p3701-0000-p3737-0000.dtb''; - gpioGuestDtbName = "tegra234-p3701-0000-gpio-passthrough.dtb"; + gpioGuestDtbName = ''tegra234-gpio-guest-passthrough.dtb''; gpioGuestDts = ./gpio_pt_guest_overlay.dtso; - copyDtb = pkgs.stdenv.mkDerivation { + copyDtbTmp = pkgs.stdenv.mkDerivation { name = "copy-dtb-file"; - buildInputs = [ pkgs.coreutils-full.cp gpioGuestOrigName gpioGuestDtbName ]; - buildPhase = ''cp ${gpioGuestOrigName} ${gpioGuestDtbName}''; + buildInputs = [ pkgs.coreutils-full gpioGuestOrigName gpioGuestDtbName ]; + buildPhase = ''cp -v ${gpioGuestOrigName} ${gpioGuestDtbName}; pwd;''; + pwd = builtins.getEnv "PWD"; outputs = [ gpioGuestDtbName ]; }; + copyDtb = builtins.trace "Evaluating copyDtb = ${copyDtbTmp} derivation in gpio-vm" copyDtbTmp; # dtbFile specifies specifically gpiovm's device tree dtbFileList = copyDtb.outputs; @@ -34,28 +36,37 @@ in { ghaf.virtualization.microvm.gpiovm.extraModules = [ { - /* - microvm.devices = [ - { - # GPIO passthrough uses a character device (/dev/vda). No need to specify? - } - ]; - */ + microvm = builtins.trace "Building ghaf.virtualization.microvm.gpiovm.extraModules.microvm" + { + /* + devices = [ + { + # GPIO passthrough uses a character device (/dev/vda). No need to specify? + } + ]; + */ - microvm.kernelParams = [ - "rootwait" - "root=/dev/vda" - ]; - # "console=ttyAMA0" # removed gpio-vm cannot open console since it does not have uarta passthough + qemu.serialConsole = false; + graphics.enable= false; + + kernelParams = builtins.trace "Evaluating kernelParams for gpio-vm" [ + "rootwait" + "root=/dev/vda" + "console=null" + ]; + # "console=ttyAMA0" # removed gpio-vm cannot open console since it does not have uarta passthough + }; - hardware.deviceTree = { + hardware.deviceTree = builtins.trace "Evaluating hardware.deviceTree for gpio-vm" { enable = true; - name = gpioGuestDtbName; - overlays = [ + #name = builtins.trace "Setting hardware.deviceTree.name to ${gpioGuestDtbName}" gpioGuestDtbName; + name = builtins.trace "Setting hardware.deviceTree.name to predefined constant string" ''tegra234-gpio-guest-passthrough.dtb''; + overlays = builtins.trace "Setting hardware.deviceTree.overlays" [ { name = "gpio_pt_guest_overlay"; dtsFile = gpioGuestDts; - filter = dtbFile; + # filter = dtbFile; + filter = gpioGuestDtbName; } ]; }; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix index bf407d61c..8af097266 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix @@ -39,6 +39,9 @@ in { name = "Bpmp Support Virtualization"; patch = ./patches/0003-bpmp-support-bpmp-virt.patch; } + # patch conflicts with gpio patches. We need to remake this patch + # At the moment its heavily hacked to not patch ./drivers/Makefile + # we let gpio do that patch for bpmp { name = "Bpmp Virt Drivers"; patch = ./patches/0004-bpmp-virt-drivers.patch; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/patches/0004-bpmp-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/patches/0004-bpmp-virt-drivers.patch index 5ccbc8124..e284e9801 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/patches/0004-bpmp-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/patches/0004-bpmp-virt-drivers.patch @@ -33,20 +33,17 @@ index dcecc9f6e33f..39e2e85ac893 100644 +endif + +endmenu -diff --git a/drivers/Makefile b/drivers/Makefile -index d3500440b928..06cb4bc1c69c 100644 ---- a/drivers/Makefile -+++ b/drivers/Makefile -@@ -191,3 +191,8 @@ obj-$(CONFIG_GNSS) += gnss/ - obj-$(CONFIG_INTERCONNECT) += interconnect/ - obj-$(CONFIG_COUNTER) += counter/ - obj-$(CONFIG_MOST) += most/ -+# -+# -+# -+obj-y += bpmp-host-proxy/ -+obj-y += bpmp-guest-proxy/ -\ No newline at end of file +# bugfix hack -- this patch conflicts with gpio patches. We let gpio do this particluar patch on ./drivers/Makefile +#diff --git a/drivers/Makefile b/drivers/Makefile +#index d3500440b928..06cb4bc1c69c 100644 +#--- a/drivers/Makefile +#+++ b/drivers/Makefile +#@@ -191,3 +191,5 @@ obj-$(CONFIG_GNSS) += gnss/ +# obj-$(CONFIG_INTERCONNECT) += interconnect/ +# obj-$(CONFIG_COUNTER) += counter/ +# obj-$(CONFIG_MOST) += most/ +#+obj-y += bpmp-host-proxy/ +#+obj-y += bpmp-guest-proxy/ diff --git a/drivers/bpmp-guest-proxy/Kconfig b/drivers/bpmp-guest-proxy/Kconfig new file mode 100644 index 000000000000..33877730ad5b diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index 9897cdb21..9ee63c7cc 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -1,8 +1,8 @@ diff --git a/drivers/Makefile b/drivers/Makefile -index d3500440b928..67f63d873ae8 100644 +index d3500440b928..ea8c07ff3700 100644 --- a/drivers/Makefile +++ b/drivers/Makefile -@@ -191,3 +191,8 @@ obj-$(CONFIG_GNSS) += gnss/ +@@ -191,3 +191,13 @@ obj-$(CONFIG_GNSS) += gnss/ obj-$(CONFIG_INTERCONNECT) += interconnect/ obj-$(CONFIG_COUNTER) += counter/ obj-$(CONFIG_MOST) += most/ @@ -11,6 +11,12 @@ index d3500440b928..67f63d873ae8 100644 +# +obj-y += gpio-host-proxy/ +obj-y += gpio-guest-proxy/ ++# ++# ++# ++obj-y += bpmp-host-proxy/ ++obj-y += bpmp-guest-proxy/ +\ No newline at end of file diff --git a/drivers/Kconfig b/drivers/Kconfig index dcecc9f6e33f..ed7d58d68ba6 100644 --- a/drivers/Kconfig diff --git a/targets/nvidia-jetson-orin/flake-module.nix b/targets/nvidia-jetson-orin/flake-module.nix index 913492289..08e14804f 100644 --- a/targets/nvidia-jetson-orin/flake-module.nix +++ b/targets/nvidia-jetson-orin/flake-module.nix @@ -58,7 +58,7 @@ enable = true; somType = som; #agx.enableNetvmWlanPCIPassthrough = som == "agx"; - #nx.enableNetvmEthernetPCIPassthrough = som == "nx"; + # nx.enableNetvmEthernetPCIPassthrough = som == "nx"; agx.enableGPIOPassthrough = som == "agx"; # removed for GPIO Passthough testing @@ -71,9 +71,9 @@ virtualization.host.bpmp.enable = lib.mkDefault false; passthroughs.host.uarta.enable = lib.mkDefault false; virtualization.host.gpio.enable = lib.mkDefault true; - # virtualization.enable = false; - # virtualization.host.bpmp.enable = false; - # passthroughs.host.uarta.enable = false; + # virtualization.enable = lib.mkDefault false; + # virtualization.host.bpmp.enable = lib.mkDefault false; + # passthroughs.host.uarta.enable = lib.mkDefault false; }; virtualization.microvm-host.enable = true; From b34766561e9e12d6806897df49f3ce671ce4dd3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Wed, 24 Apr 2024 12:04:11 +0000 Subject: [PATCH 13/26] gpio-vm running and starting up linux but with wrong qemu parameters VM kernel params are -amend rootwait console=ttyS3 No qemu.extraArgs are set --- .../agx-gpiovm-passthrough.nix | 51 +-- modules/jetpack-microvm/qemu-gpio-guestvm.dts | 396 +++++++++++++++++ .../patches/0004-gpio-virt-drivers.patch | 8 +- .../host/gpio-virt-host/default.nix | 10 +- .../patches/gpio_vm_dtb_with_vda.patch | 406 ++++++++++++++++++ .../passthrough/uarti-net-vm/default.nix | 2 +- .../microvm/virtualization/microvm/gpiovm.nix | 54 ++- .../microvm/qemu-gpio-guestvm.dtb | Bin 0 -> 7662 bytes targets/nvidia-jetson-orin/flake-module.nix | 14 +- 9 files changed, 879 insertions(+), 62 deletions(-) create mode 100644 modules/jetpack-microvm/qemu-gpio-guestvm.dts create mode 100644 modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/gpio-vm/patches/gpio_vm_dtb_with_vda.patch create mode 100644 modules/microvm/virtualization/microvm/qemu-gpio-guestvm.dtb diff --git a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix index 137dcedab..8576a35a0 100644 --- a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix +++ b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix @@ -6,37 +6,16 @@ config, ... }: let - # gpioDtbPath = ./arch/arm64/boot/dts/nvidia; - gpioGuestOrigName = ''tegra234-p3701-0000-p3737-0000.dtb''; - gpioGuestDtbName = ''tegra234-gpio-guest-passthrough.dtb''; - - gpioGuestDts = ./gpio_pt_guest_overlay.dtso; - - copyDtbTmp = pkgs.stdenv.mkDerivation { - name = "copy-dtb-file"; - buildInputs = [ pkgs.coreutils-full gpioGuestOrigName gpioGuestDtbName ]; - buildPhase = ''cp -v ${gpioGuestOrigName} ${gpioGuestDtbName}; pwd;''; - pwd = builtins.getEnv "PWD"; - outputs = [ gpioGuestDtbName ]; - }; - copyDtb = builtins.trace "Evaluating copyDtb = ${copyDtbTmp} derivation in gpio-vm" copyDtbTmp; - - # dtbFile specifies specifically gpiovm's device tree - dtbFileList = copyDtb.outputs; - dtbFile = builtins.elemAt dtbFileList 0; - cfg = config.ghaf.hardware.nvidia.orin.agx; in { options.ghaf.hardware.nvidia.orin.agx.enableGPIOPassthrough = lib.mkEnableOption "GPIO passthrough to VM"; - config = lib.mkIf cfg.enableGPIOPassthrough { - # Orin AGX GPIO Passthrough ghaf.virtualization.microvm.gpiovm.extraModules = [ { - microvm = builtins.trace "Building ghaf.virtualization.microvm.gpiovm.extraModules.microvm" + microvm = { /* devices = [ @@ -46,21 +25,25 @@ in { ]; */ - qemu.serialConsole = false; - graphics.enable= false; + # Make sure that Gpio-VM runs after the dependency service are enabled + # systemd.services."microvm@gpio-vm".after = ["gpio-dependency.service"]; - kernelParams = builtins.trace "Evaluating kernelParams for gpio-vm" [ + kernelParams = builtins.trace "Evaluating microvm.kernelParams (qemu -append) for gpio-vm" [ "rootwait" - "root=/dev/vda" - "console=null" + # "root=/dev/vda" + # gpio-vm cannot open AMA0 reserved for passthrough console + # since it does not have uarta passthough + # "console=ttyAMA0 console=ttyS0" + "console=ttyS3" ]; - # "console=ttyAMA0" # removed gpio-vm cannot open console since it does not have uarta passthough }; + /* no overlay when using dtb patch + * Note: use qemu.extraArgs for -dtb hardware.deviceTree = builtins.trace "Evaluating hardware.deviceTree for gpio-vm" { enable = true; - #name = builtins.trace "Setting hardware.deviceTree.name to ${gpioGuestDtbName}" gpioGuestDtbName; - name = builtins.trace "Setting hardware.deviceTree.name to predefined constant string" ''tegra234-gpio-guest-passthrough.dtb''; + name = builtins.trace "Setting hardware.deviceTree.name" gpioGuestDtbName; + # name = builtins.trace "Debugging with ${gpioGuestOrigName}" gpioGuestOrigName; overlays = builtins.trace "Setting hardware.deviceTree.overlays" [ { name = "gpio_pt_guest_overlay"; @@ -70,16 +53,10 @@ in { } ]; }; + */ } ]; - /* - kernel = { - inherit kernel; - #phases = _ old.phases + { name='install'; func = ''echo "do copy here"'' }; - }; - */ - /* tmp note: further kernel settings for nvidia in: ../jetpack/nvidia-jetson-orin/virtualization/default.nix ../jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix diff --git a/modules/jetpack-microvm/qemu-gpio-guestvm.dts b/modules/jetpack-microvm/qemu-gpio-guestvm.dts new file mode 100644 index 000000000..fbda540bf --- /dev/null +++ b/modules/jetpack-microvm/qemu-gpio-guestvm.dts @@ -0,0 +1,396 @@ +/dts-v1/; + +/ { + interrupt-parent = <0x8002>; + model = "linux,dummy-virt"; + #size-cells = <0x02>; + #address-cells = <0x02>; + compatible = "linux,dummy-virt"; + + psci { + migrate = <0xc4000005>; + cpu_on = <0xc4000003>; + cpu_off = <0x84000002>; + cpu_suspend = <0xc4000001>; + method = "hvc"; + compatible = "arm,psci-1.0\0arm,psci-0.2\0arm,psci"; + }; + + memory@40000000 { + reg = <0x00 0x40000000 0x00 0x8000000>; + device_type = "memory"; + }; + + platform-bus@c000000 { + interrupt-parent = <0x8002>; + ranges = <0x00 0x00 0xc000000 0x2000000>; + #address-cells = <0x01>; + #size-cells = <0x01>; + compatible = "qemu,platform\0simple-bus"; + }; + + fw-cfg@9020000 { + dma-coherent; + reg = <0x00 0x9020000 0x00 0x18>; + compatible = "qemu,fw-cfg-mmio"; + }; + + virtio_mmio@a000000 { + dma-coherent; + interrupts = <0x00 0x10 0x01>; + reg = <0x00 0xa000000 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000200 { + dma-coherent; + interrupts = <0x00 0x11 0x01>; + reg = <0x00 0xa000200 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000400 { + dma-coherent; + interrupts = <0x00 0x12 0x01>; + reg = <0x00 0xa000400 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000600 { + dma-coherent; + interrupts = <0x00 0x13 0x01>; + reg = <0x00 0xa000600 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000800 { + dma-coherent; + interrupts = <0x00 0x14 0x01>; + reg = <0x00 0xa000800 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000a00 { + dma-coherent; + interrupts = <0x00 0x15 0x01>; + reg = <0x00 0xa000a00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000c00 { + dma-coherent; + interrupts = <0x00 0x16 0x01>; + reg = <0x00 0xa000c00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000e00 { + dma-coherent; + interrupts = <0x00 0x17 0x01>; + reg = <0x00 0xa000e00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001000 { + dma-coherent; + interrupts = <0x00 0x18 0x01>; + reg = <0x00 0xa001000 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001200 { + dma-coherent; + interrupts = <0x00 0x19 0x01>; + reg = <0x00 0xa001200 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001400 { + dma-coherent; + interrupts = <0x00 0x1a 0x01>; + reg = <0x00 0xa001400 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001600 { + dma-coherent; + interrupts = <0x00 0x1b 0x01>; + reg = <0x00 0xa001600 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001800 { + dma-coherent; + interrupts = <0x00 0x1c 0x01>; + reg = <0x00 0xa001800 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001a00 { + dma-coherent; + interrupts = <0x00 0x1d 0x01>; + reg = <0x00 0xa001a00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001c00 { + dma-coherent; + interrupts = <0x00 0x1e 0x01>; + reg = <0x00 0xa001c00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001e00 { + dma-coherent; + interrupts = <0x00 0x1f 0x01>; + reg = <0x00 0xa001e00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002000 { + dma-coherent; + interrupts = <0x00 0x20 0x01>; + reg = <0x00 0xa002000 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002200 { + dma-coherent; + interrupts = <0x00 0x21 0x01>; + reg = <0x00 0xa002200 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002400 { + dma-coherent; + interrupts = <0x00 0x22 0x01>; + reg = <0x00 0xa002400 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002600 { + dma-coherent; + interrupts = <0x00 0x23 0x01>; + reg = <0x00 0xa002600 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002800 { + dma-coherent; + interrupts = <0x00 0x24 0x01>; + reg = <0x00 0xa002800 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002a00 { + dma-coherent; + interrupts = <0x00 0x25 0x01>; + reg = <0x00 0xa002a00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002c00 { + dma-coherent; + interrupts = <0x00 0x26 0x01>; + reg = <0x00 0xa002c00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002e00 { + dma-coherent; + interrupts = <0x00 0x27 0x01>; + reg = <0x00 0xa002e00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003000 { + dma-coherent; + interrupts = <0x00 0x28 0x01>; + reg = <0x00 0xa003000 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003200 { + dma-coherent; + interrupts = <0x00 0x29 0x01>; + reg = <0x00 0xa003200 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003400 { + dma-coherent; + interrupts = <0x00 0x2a 0x01>; + reg = <0x00 0xa003400 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003600 { + dma-coherent; + interrupts = <0x00 0x2b 0x01>; + reg = <0x00 0xa003600 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003800 { + dma-coherent; + interrupts = <0x00 0x2c 0x01>; + reg = <0x00 0xa003800 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003a00 { + dma-coherent; + interrupts = <0x00 0x2d 0x01>; + reg = <0x00 0xa003a00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003c00 { + dma-coherent; + interrupts = <0x00 0x2e 0x01>; + reg = <0x00 0xa003c00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003e00 { + dma-coherent; + interrupts = <0x00 0x2f 0x01>; + reg = <0x00 0xa003e00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + gpio-keys { + compatible = "gpio-keys"; + + poweroff { + gpios = <0x8004 0x03 0x00>; + linux,code = <0x74>; + label = "GPIO Key Poweroff"; + }; + }; + + pl061@9030000 { + phandle = <0x8004>; + clock-names = "apb_pclk"; + clocks = <0x8000>; + interrupts = <0x00 0x07 0x04>; + gpio-controller; + #gpio-cells = <0x02>; + compatible = "arm,pl061\0arm,primecell"; + reg = <0x00 0x9030000 0x00 0x1000>; + }; + + gpio: gpio { + compatible = "nvidia,tegra234-gpio"; + virtual-pa = <0x0 0x090c0000>; + status = "okay"; + }; + + pcie@10000000 { + interrupt-map-mask = <0x1800 0x00 0x00 0x07>; + interrupt-map = <0x00 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x05 0x04>; + #interrupt-cells = <0x01>; + ranges = <0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00>; + reg = <0x40 0x10000000 0x00 0x10000000>; + msi-map = <0x00 0x8003 0x00 0x10000>; + dma-coherent; + bus-range = <0x00 0xff>; + linux,pci-domain = <0x00>; + #size-cells = <0x02>; + #address-cells = <0x03>; + device_type = "pci"; + compatible = "pci-host-ecam-generic"; + }; + + pl031@9010000 { + clock-names = "apb_pclk"; + clocks = <0x8000>; + interrupts = <0x00 0x02 0x04>; + reg = <0x00 0x9010000 0x00 0x1000>; + compatible = "arm,pl031\0arm,primecell"; + }; + + pl011@9000000 { + clock-names = "uartclk\0apb_pclk"; + clocks = <0x8000 0x8000>; + interrupts = <0x00 0x01 0x04>; + reg = <0x00 0x9000000 0x00 0x1000>; + compatible = "arm,pl011\0arm,primecell"; + }; + + pmu { + interrupts = <0x01 0x07 0x04>; + compatible = "arm,armv8-pmuv3"; + }; + + intc@8000000 { + phandle = <0x8002>; + reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>; + #redistributor-regions = <0x01>; + compatible = "arm,gic-v3"; + ranges; + #size-cells = <0x02>; + #address-cells = <0x02>; + interrupt-controller; + #interrupt-cells = <0x03>; + + its@8080000 { + phandle = <0x8003>; + reg = <0x00 0x8080000 0x00 0x20000>; + #msi-cells = <0x01>; + msi-controller; + compatible = "arm,gic-v3-its"; + }; + }; + + flash@0 { + bank-width = <0x04>; + reg = <0x00 0x00 0x00 0x4000000 0x00 0x4000000 0x00 0x4000000>; + compatible = "cfi-flash"; + }; + + cpus { + #size-cells = <0x00>; + #address-cells = <0x01>; + + cpu-map { + + socket0 { + + cluster0 { + + core0 { + cpu = <0x8001>; + }; + }; + }; + }; + + cpu@0 { + phandle = <0x8001>; + reg = <0x00>; + compatible = "arm,arm-v8"; + device_type = "cpu"; + }; + }; + + timer { + interrupts = <0x01 0x0d 0x04 0x01 0x0e 0x04 0x01 0x0b 0x04 0x01 0x0a 0x04>; + always-on; + compatible = "arm,armv8-timer\0arm,armv7-timer"; + }; + + apb-pclk { + phandle = <0x8000>; + clock-output-names = "clk24mhz"; + clock-frequency = <0x16e3600>; + #clock-cells = <0x00>; + compatible = "fixed-clock"; + }; + + chosen { + stdout-path = "/pl011@9000000"; + rng-seed = <0x24cff097 0x781d9b28 0x94c98de 0x9f2ed06 0x6d783d1c 0xaac61e8 0x1f4d2863 0x25df18f8>; + kaslr-seed = <0x82ae7bee 0x17e8412d>; + }; +}; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index 9ee63c7cc..d6f10a0b6 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -2,7 +2,7 @@ diff --git a/drivers/Makefile b/drivers/Makefile index d3500440b928..ea8c07ff3700 100644 --- a/drivers/Makefile +++ b/drivers/Makefile -@@ -191,3 +191,13 @@ obj-$(CONFIG_GNSS) += gnss/ +@@ -191,3 +191,8 @@ obj-$(CONFIG_GNSS) += gnss/ obj-$(CONFIG_INTERCONNECT) += interconnect/ obj-$(CONFIG_COUNTER) += counter/ obj-$(CONFIG_MOST) += most/ @@ -11,12 +11,6 @@ index d3500440b928..ea8c07ff3700 100644 +# +obj-y += gpio-host-proxy/ +obj-y += gpio-guest-proxy/ -+# -+# -+# -+obj-y += bpmp-host-proxy/ -+obj-y += bpmp-guest-proxy/ -\ No newline at end of file diff --git a/drivers/Kconfig b/drivers/Kconfig index dcecc9f6e33f..ed7d58d68ba6 100644 --- a/drivers/Kconfig diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix index 74d2b3e7c..ac073d6cd 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix @@ -39,8 +39,9 @@ in { hardware.deviceTree = { # Enable hardware.deviceTree for handle host dtb overlays enable = true; - name = "tegra234-p3701-0000-p3737-0000.dtb"; - # name = "tegra234-p3701-host-passthrough.dtb"; + # name = builtin.trace "Debug dtb: tegra234-p3701-0000-p3737-0000.dtb" "tegra234-p3701-0000-p3737-0000.dtb"; + # name = pkgs.builtin.trace "Debug dtb: tegra234-p3701-host-passthrough.dtb" "tegra234-p3701-host-passthrough.dtb"; + name = "tegra234-p3701-host-passthrough.dtb"; # using overlay file: overlays = [ @@ -49,8 +50,9 @@ in { dtsFile = ./gpio_pt_host_overlay.dtso; # Apply overlay only to host passthrough device tree - filter = "tegra234-p3701-0000-p3737-0000.dtb"; - # filter = "tegra234-p3701-host-passthrough.dtb"; + # filter = builtin.trace "Debug dtb: tegra234-p3701-0000-p3737-0000.dtb" "tegra234-p3701-0000-p3737-0000.dtb"; + # filter = pkgs.builtin.trace "Debug dtb: tegra234-p3701-host-passthrough.dtb" "tegra234-p3701-host-passthrough.dtb"; + filter = "tegra234-p3701-host-passthrough.dtb"; } ]; }; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/gpio-vm/patches/gpio_vm_dtb_with_vda.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/gpio-vm/patches/gpio_vm_dtb_with_vda.patch new file mode 100644 index 000000000..3a00a80c4 --- /dev/null +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/gpio-vm/patches/gpio_vm_dtb_with_vda.patch @@ -0,0 +1,406 @@ +diff --git a/nvidia/platform/t23x/concord/kernel-dts/Makefile b/nvidia/platform/t23x/concord/kernel-dts/Makefile +--- a/nvidia/platform/t23x/concord/kernel-dts/Makefile ++++ b/nvidia/platform/t23x/concord/kernel-dts/Makefile +@@ -11,0 +12 @@ ++dtb-$(BUILD_ENABLE) += qemu-gpio-guestvm.dts +diff --git a/nvidia/platform/t23x/concord/kernel-dts/qemu-gpio-guestvm.dts b/nvidia/platform/t23x/concord/kernel-dts/qemu-gpio-guestvm.dts +new file mode 100644 +--- /dev/null ++++ b/nvidia/platform/t23x/concord/kernel-dts/qemu-gpio-guestvm.dts +@@ -0,0 +1,396 @@ ++/dts-v1/; ++ ++/ { ++ interrupt-parent = <0x8002>; ++ model = "linux,dummy-virt"; ++ #size-cells = <0x02>; ++ #address-cells = <0x02>; ++ compatible = "linux,dummy-virt"; ++ ++ psci { ++ migrate = <0xc4000005>; ++ cpu_on = <0xc4000003>; ++ cpu_off = <0x84000002>; ++ cpu_suspend = <0xc4000001>; ++ method = "hvc"; ++ compatible = "arm,psci-1.0\0arm,psci-0.2\0arm,psci"; ++ }; ++ ++ memory@40000000 { ++ reg = <0x00 0x40000000 0x00 0x8000000>; ++ device_type = "memory"; ++ }; ++ ++ platform-bus@c000000 { ++ interrupt-parent = <0x8002>; ++ ranges = <0x00 0x00 0xc000000 0x2000000>; ++ #address-cells = <0x01>; ++ #size-cells = <0x01>; ++ compatible = "qemu,platform\0simple-bus"; ++ }; ++ ++ fw-cfg@9020000 { ++ dma-coherent; ++ reg = <0x00 0x9020000 0x00 0x18>; ++ compatible = "qemu,fw-cfg-mmio"; ++ }; ++ ++ virtio_mmio@a000000 { ++ dma-coherent; ++ interrupts = <0x00 0x10 0x01>; ++ reg = <0x00 0xa000000 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a000200 { ++ dma-coherent; ++ interrupts = <0x00 0x11 0x01>; ++ reg = <0x00 0xa000200 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a000400 { ++ dma-coherent; ++ interrupts = <0x00 0x12 0x01>; ++ reg = <0x00 0xa000400 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a000600 { ++ dma-coherent; ++ interrupts = <0x00 0x13 0x01>; ++ reg = <0x00 0xa000600 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a000800 { ++ dma-coherent; ++ interrupts = <0x00 0x14 0x01>; ++ reg = <0x00 0xa000800 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a000a00 { ++ dma-coherent; ++ interrupts = <0x00 0x15 0x01>; ++ reg = <0x00 0xa000a00 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a000c00 { ++ dma-coherent; ++ interrupts = <0x00 0x16 0x01>; ++ reg = <0x00 0xa000c00 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a000e00 { ++ dma-coherent; ++ interrupts = <0x00 0x17 0x01>; ++ reg = <0x00 0xa000e00 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a001000 { ++ dma-coherent; ++ interrupts = <0x00 0x18 0x01>; ++ reg = <0x00 0xa001000 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a001200 { ++ dma-coherent; ++ interrupts = <0x00 0x19 0x01>; ++ reg = <0x00 0xa001200 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a001400 { ++ dma-coherent; ++ interrupts = <0x00 0x1a 0x01>; ++ reg = <0x00 0xa001400 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a001600 { ++ dma-coherent; ++ interrupts = <0x00 0x1b 0x01>; ++ reg = <0x00 0xa001600 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a001800 { ++ dma-coherent; ++ interrupts = <0x00 0x1c 0x01>; ++ reg = <0x00 0xa001800 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a001a00 { ++ dma-coherent; ++ interrupts = <0x00 0x1d 0x01>; ++ reg = <0x00 0xa001a00 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a001c00 { ++ dma-coherent; ++ interrupts = <0x00 0x1e 0x01>; ++ reg = <0x00 0xa001c00 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a001e00 { ++ dma-coherent; ++ interrupts = <0x00 0x1f 0x01>; ++ reg = <0x00 0xa001e00 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a002000 { ++ dma-coherent; ++ interrupts = <0x00 0x20 0x01>; ++ reg = <0x00 0xa002000 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a002200 { ++ dma-coherent; ++ interrupts = <0x00 0x21 0x01>; ++ reg = <0x00 0xa002200 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a002400 { ++ dma-coherent; ++ interrupts = <0x00 0x22 0x01>; ++ reg = <0x00 0xa002400 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a002600 { ++ dma-coherent; ++ interrupts = <0x00 0x23 0x01>; ++ reg = <0x00 0xa002600 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a002800 { ++ dma-coherent; ++ interrupts = <0x00 0x24 0x01>; ++ reg = <0x00 0xa002800 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a002a00 { ++ dma-coherent; ++ interrupts = <0x00 0x25 0x01>; ++ reg = <0x00 0xa002a00 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a002c00 { ++ dma-coherent; ++ interrupts = <0x00 0x26 0x01>; ++ reg = <0x00 0xa002c00 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a002e00 { ++ dma-coherent; ++ interrupts = <0x00 0x27 0x01>; ++ reg = <0x00 0xa002e00 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a003000 { ++ dma-coherent; ++ interrupts = <0x00 0x28 0x01>; ++ reg = <0x00 0xa003000 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a003200 { ++ dma-coherent; ++ interrupts = <0x00 0x29 0x01>; ++ reg = <0x00 0xa003200 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a003400 { ++ dma-coherent; ++ interrupts = <0x00 0x2a 0x01>; ++ reg = <0x00 0xa003400 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a003600 { ++ dma-coherent; ++ interrupts = <0x00 0x2b 0x01>; ++ reg = <0x00 0xa003600 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a003800 { ++ dma-coherent; ++ interrupts = <0x00 0x2c 0x01>; ++ reg = <0x00 0xa003800 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a003a00 { ++ dma-coherent; ++ interrupts = <0x00 0x2d 0x01>; ++ reg = <0x00 0xa003a00 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a003c00 { ++ dma-coherent; ++ interrupts = <0x00 0x2e 0x01>; ++ reg = <0x00 0xa003c00 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ virtio_mmio@a003e00 { ++ dma-coherent; ++ interrupts = <0x00 0x2f 0x01>; ++ reg = <0x00 0xa003e00 0x00 0x200>; ++ compatible = "virtio,mmio"; ++ }; ++ ++ gpio-keys { ++ compatible = "gpio-keys"; ++ ++ poweroff { ++ gpios = <0x8004 0x03 0x00>; ++ linux,code = <0x74>; ++ label = "GPIO Key Poweroff"; ++ }; ++ }; ++ ++ pl061@9030000 { ++ phandle = <0x8004>; ++ clock-names = "apb_pclk"; ++ clocks = <0x8000>; ++ interrupts = <0x00 0x07 0x04>; ++ gpio-controller; ++ #gpio-cells = <0x02>; ++ compatible = "arm,pl061\0arm,primecell"; ++ reg = <0x00 0x9030000 0x00 0x1000>; ++ }; ++ ++ gpio: gpio { ++ compatible = "nvidia,tegra234-gpio"; ++ virtual-pa = <0x0 0x090c0000>; ++ status = "okay"; ++ }; ++ ++ pcie@10000000 { ++ interrupt-map-mask = <0x1800 0x00 0x00 0x07>; ++ interrupt-map = <0x00 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x05 0x04>; ++ #interrupt-cells = <0x01>; ++ ranges = <0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00>; ++ reg = <0x40 0x10000000 0x00 0x10000000>; ++ msi-map = <0x00 0x8003 0x00 0x10000>; ++ dma-coherent; ++ bus-range = <0x00 0xff>; ++ linux,pci-domain = <0x00>; ++ #size-cells = <0x02>; ++ #address-cells = <0x03>; ++ device_type = "pci"; ++ compatible = "pci-host-ecam-generic"; ++ }; ++ ++ pl031@9010000 { ++ clock-names = "apb_pclk"; ++ clocks = <0x8000>; ++ interrupts = <0x00 0x02 0x04>; ++ reg = <0x00 0x9010000 0x00 0x1000>; ++ compatible = "arm,pl031\0arm,primecell"; ++ }; ++ ++ pl011@9000000 { ++ clock-names = "uartclk\0apb_pclk"; ++ clocks = <0x8000 0x8000>; ++ interrupts = <0x00 0x01 0x04>; ++ reg = <0x00 0x9000000 0x00 0x1000>; ++ compatible = "arm,pl011\0arm,primecell"; ++ }; ++ ++ pmu { ++ interrupts = <0x01 0x07 0x04>; ++ compatible = "arm,armv8-pmuv3"; ++ }; ++ ++ intc@8000000 { ++ phandle = <0x8002>; ++ reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>; ++ #redistributor-regions = <0x01>; ++ compatible = "arm,gic-v3"; ++ ranges; ++ #size-cells = <0x02>; ++ #address-cells = <0x02>; ++ interrupt-controller; ++ #interrupt-cells = <0x03>; ++ ++ its@8080000 { ++ phandle = <0x8003>; ++ reg = <0x00 0x8080000 0x00 0x20000>; ++ #msi-cells = <0x01>; ++ msi-controller; ++ compatible = "arm,gic-v3-its"; ++ }; ++ }; ++ ++ flash@0 { ++ bank-width = <0x04>; ++ reg = <0x00 0x00 0x00 0x4000000 0x00 0x4000000 0x00 0x4000000>; ++ compatible = "cfi-flash"; ++ }; ++ ++ cpus { ++ #size-cells = <0x00>; ++ #address-cells = <0x01>; ++ ++ cpu-map { ++ ++ socket0 { ++ ++ cluster0 { ++ ++ core0 { ++ cpu = <0x8001>; ++ }; ++ }; ++ }; ++ }; ++ ++ cpu@0 { ++ phandle = <0x8001>; ++ reg = <0x00>; ++ compatible = "arm,arm-v8"; ++ device_type = "cpu"; ++ }; ++ }; ++ ++ timer { ++ interrupts = <0x01 0x0d 0x04 0x01 0x0e 0x04 0x01 0x0b 0x04 0x01 0x0a 0x04>; ++ always-on; ++ compatible = "arm,armv8-timer\0arm,armv7-timer"; ++ }; ++ ++ apb-pclk { ++ phandle = <0x8000>; ++ clock-output-names = "clk24mhz"; ++ clock-frequency = <0x16e3600>; ++ #clock-cells = <0x00>; ++ compatible = "fixed-clock"; ++ }; ++ ++ chosen { ++ stdout-path = "/pl011@9000000"; ++ rng-seed = <0x24cff097 0x781d9b28 0x94c98de 0x9f2ed06 0x6d783d1c 0xaac61e8 0x1f4d2863 0x25df18f8>; ++ kaslr-seed = <0x82ae7bee 0x17e8412d>; ++ }; ++}; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/default.nix index 3c7b2edbf..53c5ecd5e 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/default.nix @@ -21,7 +21,7 @@ in { ''; ghaf.hardware.nvidia.virtualization.enable = true; - ghaf.virtualization.microvm.netvm.extraModules = [ + ghaf.virtualization.microvm.netvm.extraModules = builtins.trace "Setting, ghaf.virtualization.microvm.netvm.extraModules" [ { # Use serial passthrough (ttyAMA0) and virtual PCI serial (ttyS0) # as Linux console diff --git a/modules/microvm/virtualization/microvm/gpiovm.nix b/modules/microvm/virtualization/microvm/gpiovm.nix index 5dcbe852b..a786d1295 100644 --- a/modules/microvm/virtualization/microvm/gpiovm.nix +++ b/modules/microvm/virtualization/microvm/gpiovm.nix @@ -9,20 +9,25 @@ configHost = config; vmName = "gpio-vm"; + gpioGuestDtbName = ./qemu-gpio-guestvm.dtb; + tmp_rootfs = ./tegra_rootfs.qcow2; + gpiovmBaseConfiguration = { imports = [ ({lib, ...}: { ghaf = { users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; + /* development = { debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; nix-setup.enable = lib.mkDefault configHost.ghaf.development.nix-setup.enable; }; + */ systemd = { enable = true; withName = "gpiovm-systemd"; withPolkit = true; - withDebug = configHost.ghaf.profiles.debug.enable; + # withDebug = configHost.ghaf.profiles.debug.enable; }; }; @@ -31,9 +36,6 @@ nixpkgs.buildPlatform.system = configHost.nixpkgs.buildPlatform.system; nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system; - microvm.hypervisor = "qemu"; - # microvm.hypervisor.extraargs = ["--dtb", dtbfilepath]; - /* services.xxx = { # we define a servce in extraModules variable below with import ./gpio-test.nix @@ -41,6 +43,8 @@ */ microvm = { optimize.enable = true; + hypervisor = "qemu"; + shares = [ { tag = "ro-store"; @@ -48,13 +52,39 @@ mountPoint = "/nix/.ro-store"; } ]; - writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; + # writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; - /* - qemu.extraargs = [ - "--dtb" gpioGuestDtb - ]; - */ + graphics.enable= false; + qemu = { + machine = + { + # Use the same machine type as the host + x86_64-linux = "q35"; + aarch64-linux ="virt"; + } + .${configHost.nixpkgs.hostPlatform.system}; + serialConsole = true; + extraArgs = [ + # "-dtb ${gpioGuestDtbName}" + # "-monitor chardev=mon0,mode=readline" + # "-no-reboot" + ]; + /* + extraArgs = builtins.trace "Evaluating qemu.extraArgs for gpio-vm" [ + # Add custom dtb to Gpio-VM with VDA + "-dtb ${gpioGuestDtbName}" + "-monitor chardev=mon0,mode=readline" + "-no-reboot" + # "-drive file=${tmp_rootfs},if=virtio,format=qcow2" + # -nographic \ + # -machine virt,accel=kvm \ + # -cpu host \ + # -m 4G \ + # -smp 2 \ + # -kernel ${kernel} \ + ]; + */ + }; }; imports = [../../../common]; @@ -64,12 +94,12 @@ cfg = config.ghaf.virtualization.microvm.gpiovm; in { options.ghaf.virtualization.microvm.gpiovm = { - enable = lib.mkEnableOption "gpio-vm"; + enable = lib.mkEnableOption "GpioVM"; extraModules = lib.mkOption { description = '' List of additional modules to be imported and evaluated as part of - gpio-vm's NixOS configuration. + GpioVM's NixOS configuration. ''; # A service that runs a script to test gpio pins default = [ import ./gpio-test.nix { pkgs = pkgs; } ]; diff --git a/modules/microvm/virtualization/microvm/qemu-gpio-guestvm.dtb b/modules/microvm/virtualization/microvm/qemu-gpio-guestvm.dtb new file mode 100644 index 0000000000000000000000000000000000000000..00f21a374ed60ec9ab29300f7d27ea7f39c497f6 GIT binary patch literal 7662 zcmbVRJB%Df5bXhDeCGfE*v1FT$H0w!w`UoH04)JR2oMD1W^T53Z?yCC%%j2LLWJThyjLTlQWehTo*`qYYB*(PBuKR?z77ZE!S=|oW^%$9N%io1| z_q0sL3l>zAQIf}!*pQKwPlI(o91MGpFLvpV>Zg6u8@Sk>#5rdiK;NB4rsh%MXhVLI zL>XixWuNOJ%1*Jn7i@*R9fRS$rABEy8^;8DzY&@&a6HF!wfzMP>%>H89DO-V#^J!U z0jql}Oe~h&FmHBanq{D8yA|e%-7#-*WA3G|p!RW|+#T~)7T@1)2PV5o?J*nF=e;oO zz`Rc(&Vt#XK55*6dH>9q4eGPw#(Z#Q%m($zfE;o17*A?HpAG8s0T^~*KB5q3!E8{U zOrisGVP?z*_4$w+^XROY%MI$26X%Fi)ip$|=X1G1eLn2Q?9Lo#gZkuRam0CiX3Pfl z`KTN7#LSot>XQr95hq@^6}6ww2KDK?F`t+jvq62bC>(J_jiQY~}-WAHo9Z`ml=P(3z3VI+He`ygonJs|jB+I6F?zpX%a z#U|3qeANl-b5j*ut&?_`pE^NjhaPpd>tXB;J^H_05Bv_jy;_ecn2^gqgFB_?^3UK- z>AC!K;kx`Yc~SFg9d!9;aHsTK{u$gUJ(qtjT$g_)FZMk|x#;rG;7;ke{4=;ydM^K5 zxGw+fIxu#<`n&k6@OB-}PSMl<85ROKsK{o^-j70u20@$ZcjujAs(901`J&nd@2_TM<;yTg{Gm)` z5rq(d7&iC9W$uNl+*P--HOt;ddz>#~+nk8GN#a#jD68*%%Z|Oz`n5#Aw(UXuHh6v< z6cx8*$KK3>{8CTTCYs8Z`uey7Hx>8;?$+NUntUR0HEUj+cx%9Zbw^J4tkck4V+Cc54_ z+SXS#&l%YbxYzvl0fipzBQ1D?I4D zrKRU5%qQRH4Th>eYFNI5HvRXY>nA}@nOA1vNLI=>FO0`!B@4wUy{wRiF*ZD$Up8rj zan&!{)Ddpsab4JaF%%mkZB6DHYt3IfiJq--BiJHu?Rymt8wGo`4qf6IhPJQ6McZui zvU&T$OHb7KZ zoyt;u!?BobIWBXV_C+GA)vPZHITU@l7KQRuwUJ9v1nE$gqMrnQn5{~MJaMHIJQj3z_&p3z?p(v}MLeWg1 zSQEI)(R7#SY-wm4Ng1j7!Czv&a^85y=T|>Vf+!W8LiVGwDx#Hfl@&fhj Date: Thu, 2 May 2024 07:26:39 +0000 Subject: [PATCH 14/26] Partially back tracking to older version on tiiuae/ghaf on git Recent merge is partially retrenched. Newer files breaks this GPIO version because the host dtb overlay is broken by netvm-wlan-pci-passthrough. The changes in tiiuae git are realated to pci passthrough, optee kernel, "machine" option for microvm/qemu and a patch provided for netvm-wlan-pci-passthrough --- flake.lock | 92 +++---- mk_0003.sh | 37 +++ mk_patches.sh | 37 ++- modules/common/profiles/release.nix | 2 + .../agx-gpiovm-passthrough.nix | 12 +- .../agx-netvm-wlan-pci-passthrough.nix | 4 +- .../nx-netvm-ethernet-pci-passthrough.nix | 4 +- .../pci-passthrough-agx-test.patch | 10 +- .../patches/0003-gpio-virt-kernel.patch | 227 ++++++++---------- .../patches/0004-gpio-virt-drivers.patch | 145 +++++------ .../host/gpio-virt-host/default.nix | 12 +- .../passthrough/uarti-net-vm/default.nix | 6 +- .../microvm/virtualization/microvm/appvm.nix | 22 +- .../microvm/virtualization/microvm/gpiovm.nix | 14 +- .../microvm/virtualization/microvm/guivm.nix | 18 +- .../microvm/virtualization/microvm/netvm.nix | 12 +- .../microvm/qemu-gpio-guestvm.dtb | Bin 7662 -> 8946 bytes .../microvm/simple-chardev-test.sh | 26 +- targets/nvidia-jetson-orin/flake-module.nix | 13 +- targets/nvidia-jetson-orin/optee.nix | 4 +- 20 files changed, 357 insertions(+), 340 deletions(-) create mode 100755 mk_0003.sh diff --git a/flake.lock b/flake.lock index 95cd65489..76569b6c8 100644 --- a/flake.lock +++ b/flake.lock @@ -41,11 +41,11 @@ ] }, "locked": { - "lastModified": 1713195852, - "narHash": "sha256-MEb4Hx/Aw7pcsmcHXBuldFsrVTfl9Q9dz1JSlxUanmE=", + "lastModified": 1705332421, + "narHash": "sha256-USpGLPme1IuqG78JNqSaRabilwkCyHmVWY0M9vYyqEA=", "owner": "numtide", "repo": "devshell", - "rev": "2c8e04e5c29299bec53c2e5a73da0f9afa8dabb5", + "rev": "83cb93d6d063ad290beee669f4badf9914cc16ec", "type": "github" }, "original": { @@ -61,11 +61,11 @@ ] }, "locked": { - "lastModified": 1713204594, - "narHash": "sha256-5yyHYBWFZUKXkJvOccPBeX83hH2iED54NLnWs2eWgS0=", + "lastModified": 1712612224, + "narHash": "sha256-Tv4C8OSPVmm4LbpJGLFSODyvJy6DqrisEGPCQdNVOeY=", "owner": "nix-community", "repo": "disko", - "rev": "d51114dc1bf3cfaba2b6644aabd16ff0c9909af5", + "rev": "79eab0e82cb126bf4ac170f44af82479f0895ab5", "type": "github" }, "original": { @@ -98,11 +98,11 @@ ] }, "locked": { - "lastModified": 1712014858, - "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "lastModified": 1706830856, + "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", "type": "github" }, "original": { @@ -151,11 +151,11 @@ ] }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", "type": "github" }, "original": { @@ -172,11 +172,11 @@ ] }, "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "lastModified": 1703887061, + "narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5", "type": "github" }, "original": { @@ -192,11 +192,11 @@ ] }, "locked": { - "lastModified": 1712200868, - "narHash": "sha256-eSBfjRL6cOhGcawxRhkdNxalRXMPimeVO16W2Jsp4WA=", + "lastModified": 1707323143, + "narHash": "sha256-Mfj2l2aE+3Vu/u1M1PtQTvIoOZfCkINgtCQagSZFU6Q=", "owner": "anduril", "repo": "jetpack-nixos", - "rev": "99ff19f877ad05dc4d171a1f543a90b997cef240", + "rev": "6ae4ce1d368fb56235a8b15ef926db28c4643eb8", "type": "github" }, "original": { @@ -251,11 +251,11 @@ "spectrum": "spectrum" }, "locked": { - "lastModified": 1713189110, - "narHash": "sha256-c/yG/AsPmMBMe4RAxn4KOkOaR4rsW5s3AjtfriOQKD8=", + "lastModified": 1707953231, + "narHash": "sha256-xdhJQH4ER3lqaNJ+ZxhmNhjFn47HIsWdSpzWhl6dRAY=", "owner": "astro", "repo": "microvm.nix", - "rev": "a1341f7195e34d9bb88c12314cc3b0c4429f9b0a", + "rev": "d350318cb7f40a300b4b4674acf9bee26933ecca", "type": "github" }, "original": { @@ -277,11 +277,11 @@ ] }, "locked": { - "lastModified": 1709911523, - "narHash": "sha256-XNutwbRI6h57ybeKy0yYupfngWYcfcIqE0b0LgXnyxs=", + "lastModified": 1703607026, + "narHash": "sha256-Emh0BPoqlS4ntp2UJrwydXfIP4qIMF0VBB2FUE3/M/E=", "owner": "Mic92", "repo": "nix-fast-build", - "rev": "692fe3e98f36b60c678d637235271b57910a7f80", + "rev": "4376b8a33b217ee2f78ba3dcff01a3e464d13a46", "type": "github" }, "original": { @@ -292,11 +292,11 @@ }, "nixlib": { "locked": { - "lastModified": 1712450863, - "narHash": "sha256-K6IkdtMtq9xktmYPj0uaYc8NsIqHuaAoRBaMgu9Fvrw=", + "lastModified": 1693701915, + "narHash": "sha256-waHPLdDYUOHSEtMKKabcKIMhlUOHPOOPQ9UyFeEoovs=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "3c62b6a12571c9a7f65ab037173ee153d539905f", + "rev": "f5af57d3ef9947a70ac86e42695231ac1ad00c25", "type": "github" }, "original": { @@ -313,11 +313,11 @@ ] }, "locked": { - "lastModified": 1712537332, - "narHash": "sha256-yYlxv1sg/TNl6hghjAe0ct+/p5PwXiT1mpuaExjhR88=", + "lastModified": 1707873059, + "narHash": "sha256-simzllUEmzVqmQogcGCorfIbJpodAhgGSr6vuFtd4XQ=", "owner": "nix-community", "repo": "nixos-generators", - "rev": "d942db8df8ee860556a38754f15b8d03bf7e6933", + "rev": "0aa24e93f75370454f0e03747b6836ac2a2c9fca", "type": "github" }, "original": { @@ -328,11 +328,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1712909959, - "narHash": "sha256-7/5ubuwdEbQ7Z+Vqd4u0mM5L2VMNDsBh54visp27CtQ=", + "lastModified": 1707842204, + "narHash": "sha256-M+HAq1qWQBi/gywaMZwX0odU+Qb/XeqVeANGKRBDOwU=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "f58b25254be441cd2a9b4b444ed83f1e51244f1f", + "rev": "f1b2f71c86a5b1941d20608db0b1e88a07d31303", "type": "github" }, "original": { @@ -343,11 +343,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1713145326, - "narHash": "sha256-m7+IWM6mkWOg22EC5kRUFCycXsXLSU7hWmHdmBfmC3s=", + "lastModified": 1707786466, + "narHash": "sha256-yLPfrmW87M2qt+8bAmwopJawa+MJLh3M9rUbXtpUc1o=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "53a2c32bc66f5ae41a28d7a9a49d321172af621e", + "rev": "01885a071465e223f8f68971f864b15829988504", "type": "github" }, "original": { @@ -374,11 +374,11 @@ ] }, "locked": { - "lastModified": 1712897695, - "narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=", + "lastModified": 1707297608, + "narHash": "sha256-ADjo/5VySGlvtCW3qR+vdFF4xM9kJFlRDqcC9ZGI8EA=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8", + "rev": "0db2e67ee49910adfa13010e7f012149660af7f0", "type": "github" }, "original": { @@ -435,11 +435,11 @@ "spectrum": { "flake": false, "locked": { - "lastModified": 1708358594, - "narHash": "sha256-e71YOotu2FYA67HoC/voJDTFsiPpZNRwmiQb4f94OxQ=", + "lastModified": 1703273931, + "narHash": "sha256-CJ1Crdi5fXHkCiemovsp20/RC4vpDaZl1R6V273FecI=", "ref": "refs/heads/main", - "rev": "6d0e73864d28794cdbd26ab7b37259ab0e1e044c", - "revCount": 614, + "rev": "97e2f3429ee61dc37664b4d096b2fec48a57b691", + "revCount": 597, "type": "git", "url": "https://spectrum-os.org/git/spectrum" }, @@ -485,11 +485,11 @@ ] }, "locked": { - "lastModified": 1711963903, - "narHash": "sha256-N3QDhoaX+paWXHbEXZapqd1r95mdshxToGowtjtYkGI=", + "lastModified": 1707300477, + "narHash": "sha256-qQF0fEkHlnxHcrKIMRzOETnRBksUK048MXkX0SOmxvA=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "49dc4a92b02b8e68798abd99184f228243b6e3ac", + "rev": "ac599dab59a66304eb511af07b3883114f061b9d", "type": "github" }, "original": { diff --git a/mk_0003.sh b/mk_0003.sh new file mode 100755 index 000000000..1b5e6b326 --- /dev/null +++ b/mk_0003.sh @@ -0,0 +1,37 @@ +home="/home/$(id -un)" +# sw=${PWD} +sw="${home}/software" +tegra="${home}/software/Jetson/Linux_for_Tegra" +kern="${tegra}/sources/kernel" +ghaf="${sw}/ghaf" +#patchdir="${ghaf}/modules/hardware/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches" +patchdir="${ghaf}/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/" + + +# create empty tree to git diff against +empty=$(git hash-object -t tree /dev/null) + +# ------ + +pushd $kern + +# ------ +echo "this script updates only the 0003-gpio-virt-kernel.patch, before rebuilding Ghaf" + +# 0003-gpio-virt-kernel.patch # exclude /drive/Kconfig and drive/Makefile +git -C kernel-5.10/ diff basepoint -- drivers/gpio/ \ + >${patchdir}/0003-gpio-virt-kernel.patch +git -C kernel-5.10/ diff basepoint -- drivers/pinctrl/ \ + >>${patchdir}/0003-gpio-virt-kernel.patch +git -C kernel-5.10/ diff basepoint -- include/ \ + >>${patchdir}/0003-gpio-virt-kernel.patch + +# ------ + +# build ghaf +cd ${ghaf} +./build.sh + +# ------ + +popd diff --git a/mk_patches.sh b/mk_patches.sh index 10c43171d..08ead6290 100755 --- a/mk_patches.sh +++ b/mk_patches.sh @@ -20,32 +20,48 @@ empty=$(git hash-object -t tree /dev/null) pushd $kern # ------ +# preparatory steps # create patch for merged Kconfig and Makefile in gpio-virt # merge gpio-virt/drivers and kernel-5.10/drivers Makefile and Kconfig file +# stash away original Kconfig and Makefie files cp gpio-virt/drivers/Kconfig /tmp/original_Kconfig cp gpio-virt/drivers/Makefile /tmp/original_Makefile # "cat" option leaves functions in gpio-virt #undeclared #cat kernel-5.10/drivers/Kconfig gpio-virt/drivers/Kconfig >gpio-virt/drivers/tmp_Kconfig +# +# Make concatenated Kconfig grep -veendmenu kernel-5.10/drivers/Kconfig >gpio-virt/drivers/tmp_Kconfig grep -veappend_menu gpio-virt/drivers/Kconfig >>gpio-virt/drivers/tmp_Kconfig mv gpio-virt/drivers/tmp_Kconfig gpio-virt/drivers/Kconfig +# Make concatenated Makefile cat kernel-5.10/drivers/Makefile gpio-virt/drivers/Makefile >gpio-virt/drivers/tmp_Makefile mv gpio-virt/drivers/tmp_Makefile gpio-virt/drivers/Makefile # Merged patch should be made against files in kernel-5-10 # dont use ${empty} -- instead diff against merged files -git -C kernel-5.10/ diff -- drivers/Makefile ../gpio-virt/drivers/Makefile >${ghaf}/raw_MK_drivers.patch -git -C kernel-5.10/ diff -- drivers/Kconfig ../gpio-virt/drivers/Kconfig >>${ghaf}/raw_MK_drivers.patch + +# diff Makefile and Kconfig +#git -C kernel-5.10/ diff -- drivers/Makefile ../gpio-virt/drivers/Makefile >${ghaf}/raw_MK_drivers.patch +git -C kernel-5.10/ diff -- drivers/Kconfig ../gpio-virt/drivers/Kconfig >>${ghaf}/raw_MK_drivers.patch +# remove path to gpio-virt sed -i -e's/..\/gpio-virt\///' ${ghaf}/raw_MK_drivers.patch -# restore Kconfig and Makefile used in merged patch +# make hacked -u0 diff for Makefile and Kconfig +echo "diff --git a/drivers/Makefile b/drivers/Makefile" >${ghaf}/raw_u0_MK_drivers.patch +diff -u0 kernel-5.10/drivers/Makefile gpio-virt/drivers/Makefile >>${ghaf}/raw_u0_MK_drivers.patch +#echo "diff --git a/drivers/Kconfig b/drivers/Kconfig" >>${ghaf}/raw_u0_MK_drivers.patch +#diff -u0 kernel-5.10/drivers/Kconfig gpio-virt/drivers/Kconfig >>${ghaf}/raw_u0_MK_drivers.patch +sed -i -e's/kernel-5.10/a/;s/gpio-virt/b/' ${ghaf}/raw_u0_MK_drivers.patch + +# restore stashed Kconfig and Makefile used in merged patch mv /tmp/original_Kconfig gpio-virt/drivers/Kconfig mv /tmp/original_Makefile gpio-virt/drivers/Makefile +#end of preparatory steps # ------ # 0002-vfio_platform-reset-required-false.patch # not needed because of kernel boot parameters @@ -62,11 +78,18 @@ git -C kernel-5.10/ diff basepoint -- include/ \ # ------ +# do not make the 0004 patch because it needs to be made with diff context 0, not to clash with bpmp patch. +# The clash concerns only the first git diff +# diff --git a/drivers/Makefile b/drivers/Makefile +# edit the output of 'diff -u0' in its place +# or directly edit the patchfie + # 0004-gpio-virt-drivers.patch -# include merged Kconfig and Makefile by using raw_MK_drivers.patch (note '>>') -mv ${ghaf}/raw_MK_drivers.patch ${patchdir}/0004-gpio-virt-drivers.patch -git -C gpio-virt/ diff ${empty} -- "drivers/gpio*" \ - >>${patchdir}/0004-gpio-virt-drivers.patch +# include merged Kconfig and Makefile by using raw_MK_drivers.patch and raw_u0_MK_drivers.patch (note '>' and '>>') +cat ${ghaf}/raw_MK_drivers.patch ${ghaf}/raw_u0_MK_drivers.patch >${patchdir}/0004-gpio-virt-drivers.patch +git -C gpio-virt/ diff ${empty} -- "drivers/gpio*" >>${patchdir}/0004-gpio-virt-drivers.patch + +rm ${ghaf}/raw_MK_drivers.patch ${ghaf}/raw_u0_MK_drivers.patch # ------ diff --git a/modules/common/profiles/release.nix b/modules/common/profiles/release.nix index 014f7ff76..190720bd4 100644 --- a/modules/common/profiles/release.nix +++ b/modules/common/profiles/release.nix @@ -13,6 +13,8 @@ in enable = mkEnableOption "release profile"; }; + options.ghaf.time.timeZone = "Europe/Helsinki"; + config = mkIf cfg.enable { # Enable default accounts and passwords # TODO this needs to be refined when we define a policy for the diff --git a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix index 8576a35a0..33a409c88 100644 --- a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix +++ b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix @@ -28,23 +28,23 @@ in { # Make sure that Gpio-VM runs after the dependency service are enabled # systemd.services."microvm@gpio-vm".after = ["gpio-dependency.service"]; - kernelParams = builtins.trace "Evaluating microvm.kernelParams (qemu -append) for gpio-vm" [ + kernelParams = builtins.trace "GpioVM: Evaluating microvm.kernelParams (qemu -append) for gpio-vm" [ "rootwait" # "root=/dev/vda" # gpio-vm cannot open AMA0 reserved for passthrough console # since it does not have uarta passthough # "console=ttyAMA0 console=ttyS0" - "console=ttyS3" + # "console=tty10" ]; }; /* no overlay when using dtb patch * Note: use qemu.extraArgs for -dtb - hardware.deviceTree = builtins.trace "Evaluating hardware.deviceTree for gpio-vm" { + hardware.deviceTree = builtins.trace "GpioVM: Evaluating hardware.deviceTree for gpio-vm" { enable = true; - name = builtins.trace "Setting hardware.deviceTree.name" gpioGuestDtbName; - # name = builtins.trace "Debugging with ${gpioGuestOrigName}" gpioGuestOrigName; - overlays = builtins.trace "Setting hardware.deviceTree.overlays" [ + name = builtins.trace "GpioVM: Setting hardware.deviceTree.name" gpioGuestDtbName; + # name = builtins.trace "GpioVM: Debugging with ${gpioGuestOrigName}" gpioGuestOrigName; + overlays = builtins.trace "GpioVM: Setting hardware.deviceTree.overlays" [ { name = "gpio_pt_guest_overlay"; dtsFile = gpioGuestDts; diff --git a/modules/jetpack-microvm/agx-netvm-wlan-pci-passthrough.nix b/modules/jetpack-microvm/agx-netvm-wlan-pci-passthrough.nix index a5a60e237..6c2ff08bc 100644 --- a/modules/jetpack-microvm/agx-netvm-wlan-pci-passthrough.nix +++ b/modules/jetpack-microvm/agx-netvm-wlan-pci-passthrough.nix @@ -25,19 +25,21 @@ in { } ]; + /* tmp removed for GPIO testing boot.kernelPatches = [ { name = "agx-pci-passthrough-patch"; patch = ./pci-passthrough-agx-test.patch; } ]; + */ boot.kernelParams = [ "vfio-pci.ids=10ec:c82f" "vfio_iommu_type1.allow_unsafe_interrupts=1" ]; - hardware.deviceTree = lib.mkDefault { + hardware.deviceTree = builtins.trace "netvm-wlan-pci-passthrough setting default deviceTree" lib.mkDefault { enable = true; name = "tegra234-p3701-host-passthrough.dtb"; }; diff --git a/modules/jetpack-microvm/nx-netvm-ethernet-pci-passthrough.nix b/modules/jetpack-microvm/nx-netvm-ethernet-pci-passthrough.nix index 6c22899b7..5ff7e906a 100644 --- a/modules/jetpack-microvm/nx-netvm-ethernet-pci-passthrough.nix +++ b/modules/jetpack-microvm/nx-netvm-ethernet-pci-passthrough.nix @@ -29,6 +29,7 @@ in { } ]; + /* tmp removed for GPIO testing boot.kernelPatches = [ { name = "nx-pci-passthrough-patch"; @@ -37,13 +38,14 @@ in { patch = ./pci-passthrough-nx-test.patch; } ]; + */ boot.kernelParams = [ "vfio-pci.ids=10ec:8168" "vfio_iommu_type1.allow_unsafe_interrupts=1" ]; - hardware.deviceTree = { + hardware.deviceTree = builtins.trace "netvm-ethernet-pci-passthrough setting default deviceTree" { enable = true; name = "tegra234-p3767-host-passthrough.dtb"; }; diff --git a/modules/jetpack-microvm/pci-passthrough-agx-test.patch b/modules/jetpack-microvm/pci-passthrough-agx-test.patch index bc3f5ac34..bb51239a5 100644 --- a/modules/jetpack-microvm/pci-passthrough-agx-test.patch +++ b/modules/jetpack-microvm/pci-passthrough-agx-test.patch @@ -1,11 +1,11 @@ diff --git a/nvidia/platform/t23x/concord/kernel-dts/Makefile b/nvidia/platform/t23x/concord/kernel-dts/Makefile -index 8e040d61f72f..dace3ef9a86d 100644 +index 1be5b3f76bf8..01d3dea90cb5 100644 --- a/nvidia/platform/t23x/concord/kernel-dts/Makefile +++ b/nvidia/platform/t23x/concord/kernel-dts/Makefile -@@ -25,6 +25,9 @@ dtb-$(BUILD_ENABLE) += tegra234-p3701-0000-p3737-0000-kexec.dtb +@@ -23,6 +23,9 @@ dtb-$(BUILD_ENABLE) += tegra234-p3701-0000-as-p3767-0001-p3737-0000.dtb + dtb-$(BUILD_ENABLE) += tegra234-p3701-0000-as-pxxxx-p3737-0000.dtb + dtb-$(BUILD_ENABLE) += tegra234-p3701-0000-p3737-0000-kexec.dtb dtb-$(BUILD_ENABLE) += tegra234-p3701-0004-p3737-0000.dtb - dtb-$(BUILD_ENABLE) += tegra234-p3701-0005-p3737-0000.dtb - dtb-$(BUILD_ENABLE) += tegra234-p3701-0008-p3737-0000.dtb + +dtb-$(BUILD_ENABLE) += tegra234-p3701-host-passthrough.dtb + @@ -14,7 +14,7 @@ index 8e040d61f72f..dace3ef9a86d 100644 dtbo-$(BUILD_ENABLE) += tegra234-p3737-audio-codec-rt5640.dtbo diff --git a/nvidia/platform/t23x/concord/kernel-dts/tegra234-p3701-host-passthrough.dts b/nvidia/platform/t23x/concord/kernel-dts/tegra234-p3701-host-passthrough.dts new file mode 100644 -index 000000000000..5e9a0885d318 +index 000000000000..e4656287da82 --- /dev/null +++ b/nvidia/platform/t23x/concord/kernel-dts/tegra234-p3701-host-passthrough.dts @@ -0,0 +1,12 @@ diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch index 79062ec33..823b3c631 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch @@ -247,7 +247,7 @@ index f66fc17faee4..629bc13aa4c3 100644 } subsys_initcall(tegra_gpio_init); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c -index 5e57824b283e..e56a6b06f91e 100644 +index 5e57824b283e..581ba1f606d8 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -44,7 +44,7 @@ @@ -273,7 +273,7 @@ index 5e57824b283e..e56a6b06f91e 100644 + #include + + #define GPIO_DEBUG -+ #define GPIO_DEBUG_VERBOSE ++ // #define GPIO_DEBUG_VERBOSE + + #ifdef GPIO_DEBUG + #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) @@ -624,13 +624,13 @@ index 5e57824b283e..e56a6b06f91e 100644 for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; unsigned int offset, p = port->port; -@@ -1107,6 +1211,177 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +@@ -1107,6 +1211,188 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) return -EINVAL; } +#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) + -+ // functoons that are passed through. Function body is in gpio-guest-proxy.c ++ // functions that are passed through. Function body is in gpio-guest-proxy.c + extern int gpiochip_generic_request_redirect(struct gpio_chip *gc, unsigned offset); + + extern void gpiochip_generic_free_redirect(struct gpio_chip *gc, unsigned offset); @@ -671,7 +671,7 @@ index 5e57824b283e..e56a6b06f91e 100644 + + extern int tegra186_gpio_add_pin_ranges_redirect(struct gpio_chip *chip); + -+ inline void gpio_hook(struct tegra_gpio *gpio) { ++ static inline void gpio_hook(struct tegra_gpio *gpio) { + gpio->gpio.request = gpiochip_generic_request_redirect; + gpio->gpio.free = gpiochip_generic_free_redirect; + gpio->gpio.get_direction = tegra186_gpio_get_direction_redirect; @@ -685,25 +685,33 @@ index 5e57824b283e..e56a6b06f91e 100644 + gpio->gpio.suspend_configure = tegra_gpio_suspend_configure_redirect; + gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges_redirect; + gpio->gpio.base = -1; -+ } + -+ inline void gpio_unhook(struct tegra_gpio *gpio) { -+ gpio->gpio.request = gpiochip_generic_request; -+ gpio->gpio.free = gpiochip_generic_free; -+ gpio->gpio.get_direction = tegra186_gpio_get_direction; -+ gpio->gpio.direction_input = tegra186_gpio_direction_input; -+ gpio->gpio.direction_output = tegra186_gpio_direction_output; -+ gpio->gpio.get = tegra186_gpio_get; -+ gpio->gpio.set = tegra186_gpio_set; -+ gpio->gpio.set_config = tegra186_gpio_set_config; -+ gpio->gpio.timestamp_control = tegra_gpio_timestamp_control; -+ gpio->gpio.timestamp_read = tegra_gpio_timestamp_read; -+ gpio->gpio.suspend_configure = tegra_gpio_suspend_configure; -+ gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; -+ gpio->gpio.base = -1; ++ deb_debug("gpio functions are hooked\n"); + } ++#endif ++ ++// this function sets the standard bindings used by the host driver ++static inline void gpio_unhook(struct tegra_gpio *gpio) { ++ gpio->gpio.request = gpiochip_generic_request; ++ gpio->gpio.free = gpiochip_generic_free; ++ gpio->gpio.get_direction = tegra186_gpio_get_direction; ++ gpio->gpio.direction_input = tegra186_gpio_direction_input; ++ gpio->gpio.direction_output = tegra186_gpio_direction_output; ++ gpio->gpio.get = tegra186_gpio_get; ++ gpio->gpio.set = tegra186_gpio_set; ++ gpio->gpio.set_config = tegra186_gpio_set_config; ++ gpio->gpio.timestamp_control = tegra_gpio_timestamp_control; ++ gpio->gpio.timestamp_read = tegra_gpio_timestamp_read; ++ gpio->gpio.suspend_configure = tegra_gpio_suspend_configure; ++ gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; ++ gpio->gpio.base = -1; ++ ++ deb_debug("gpio functions are unhooked\n"); ++} ++ ++#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) + -+ extern int tegra_gpio_guest_init(struct gpio_chip *gpio); ++ extern int tegra_gpio_guest_init(void); + + #define MAX_CHIP 2 // check this value against value in gpio_host-proxy.h + @@ -743,6 +751,9 @@ index 5e57824b283e..e56a6b06f91e 100644 + static void preserve_tegrachip(struct tegra_gpio *tegrachip) { + struct gpio_chip *gpiochip = &tegrachip->gpio; + int id = gpiochip->gpiodev->id; ++ ++ deb_debug("id = %d\n", id); ++ + if( id != gpio_chip_count) { + // we assume gpiochip0 will be registered in slot 0 and gpiochip1 in slot 1 + // if this nonfatal error triggers, we register using 'id' as an index ansyhow @@ -802,15 +813,13 @@ index 5e57824b283e..e56a6b06f91e 100644 static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; -@@ -1120,17 +1395,57 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1120,17 +1406,26 @@ static int tegra186_gpio_probe(struct platform_device *pdev) int value; void __iomem *base; -+ #if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) -+ bool guest_proxy = false; -+ #endif -+ -+ deb_debug("\n"); ++ static bool guest_proxy_is_set_up = false; ++ ++ deb_debug("Probing gpio\n"); + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); - if (!gpio) @@ -823,35 +832,6 @@ index 5e57824b283e..e56a6b06f91e 100644 gpio->gpio.label = gpio->soc->name; gpio->gpio.parent = &pdev->dev; -+ #if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) -+ -+ deb_debug("GPIO Proxy code\n"); -+ -+ // If virtual-pa node is defined, it means that we are using a virtual GPIO -+ // then we have to initialise the gpio-guest -+ err = of_property_read_u64(pdev->dev.of_node, "virtual-pa", &gpio_vpa); -+ if(!err){ -+ // code executed in gpio-guest only -+ deb_info("GPIO virtual-pa: 0x%llx\n", gpio_vpa); -+ /* we are now running gpio-guest-proxy code */ -+ guest_proxy = true; -+ ret = tegra_gpio_guest_init(&gpio->gpio); -+ gpio_hook(gpio); -+ // unpreserve_all_tegrachips() will unhook functions, if it ever was called -+ -+ // we need to handle irq? -+ -+ // is the assumption on next comment line not valid for gpio? (because of interrupts) -+ // in guest proxy driver subsequent code is redundant -- thus return -+ return ret; -+ } -+ // we assune that guest proxy code will not execute here -+ // that is maybe a false assumption that further setup is unecessary -+ // or maybe not, we need to register gpio with pdev for the passthough functions -+ BUG_ON(gpio_vpa != 0); -+ -+ #endif -+ gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security"); - if (IS_ERR(gpio->secure)) + if (IS_ERR(gpio->secure)) { @@ -862,7 +842,7 @@ index 5e57824b283e..e56a6b06f91e 100644 /* count the number of banks in the controller */ for (i = 0; i < gpio->soc->num_ports; i++) -@@ -1194,21 +1509,31 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1194,21 +1489,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->irq[i] = err; } @@ -881,35 +861,44 @@ index 5e57824b283e..e56a6b06f91e 100644 - gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; - - gpio->gpio.base = -1; -+ #ifdef CONFIG_TEGRA_GPIO_HOST_PROXY -+ // guest proxy guard just in case we execute this section later as guest (we should not) -+ if(!guest_proxy) { -+ #endif -+ // gpio_unhook is the same as these standard settings -+ // these pointers are host only -+ gpio->gpio.request = gpiochip_generic_request; -+ gpio->gpio.free = gpiochip_generic_free; -+ gpio->gpio.get_direction = tegra186_gpio_get_direction; -+ gpio->gpio.direction_input = tegra186_gpio_direction_input; -+ gpio->gpio.direction_output = tegra186_gpio_direction_output; -+ gpio->gpio.get = tegra186_gpio_get; -+ gpio->gpio.set = tegra186_gpio_set; -+ gpio->gpio.set_config = tegra186_gpio_set_config; -+ gpio->gpio.timestamp_control = tegra_gpio_timestamp_control; -+ gpio->gpio.timestamp_read = tegra_gpio_timestamp_read; -+ gpio->gpio.suspend_configure = tegra_gpio_suspend_configure; -+ gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; -+ gpio->gpio.base = -1; -+ #ifdef CONFIG_TEGRA_GPIO_HOST_PROXY -+ } -+ else { -+ deb_info("guest driver found executing host code"); -+ } ++ #if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++ ++ deb_debug("GPIO Proxy code\n"); ++ ++ // If virtual-pa node is defined, it means that we are using a virtual GPIO ++ // then we have to initialise the gpio-guest ++ err = of_property_read_u64(pdev->dev.of_node, "virtual-pa", &gpio_vpa); ++ // code behind 'if' is executed in guest VM based on Device Tree parsing of virtual-pa above ++ if(!err) { ++ deb_info("GPIO virtual-pa: 0x%llx\n", gpio_vpa); ++ if( ! guest_proxy_is_set_up ) { ++ // we want to avoid double initialisation of tegra_gpio_guest_init() ++ ret = tegra_gpio_guest_init(); ++ guest_proxy_is_set_up = true; ++ } ++ // hook for all instances of "host" (i.e. default) driver in guest VM" ++ gpio_hook(gpio); ++ } ++ // error in reading virtual-pa is non fatal, it only means we are on host. ++ else { ++ // gpio_unhook is the same as standard settings ++ // unhooked pointers are for the host driver on host only ++ // guest should use gpio_hook() for its host driver ++ BUG_ON(gpio_vpa != 0); // assert we do not set up the vpa driver ++ gpio_unhook(gpio); ++ } ++ gpio->gpio.base = -1; ++ #else ++ deb_debug("Setting standard gpio functions in a non-proxy compile of driver\n") ++ BUG_ON(gpio_vpa != 0); // assert we do not set up the vpa driver ++ gpio_unhook(gpio); + #endif ++ ++ deb_debug("gpio functions are set\n"); for (i = 0; i < gpio->soc->num_ports; i++) gpio->gpio.ngpio += gpio->soc->ports[i].pins; -@@ -1229,6 +1554,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1229,6 +1543,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -ENOMEM; names[offset + j] = name; @@ -917,45 +906,19 @@ index 5e57824b283e..e56a6b06f91e 100644 } offset += port->pins; -@@ -1343,10 +1669,46 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1343,10 +1658,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) if (gpio->use_timestamp) tegra_gte_setup(gpio); + #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) + + deb_debug("GPIO, initialised gpio label=%s\n", gpio->gpio.label); -+ deb_debug("GPIO, initialised gpio at %p\n", gpio); -+ deb_debug("GPIO, initialised gpio->secure at %p\n", gpio->secure); -+ deb_debug("GPIO, initialised gpio->base at %p\n", gpio->base); -+ deb_debug("GPIO, initialised gpio->gte_regs at %p\n", gpio->gte_regs); ++ // deb_debug("GPIO, initialised gpio at %p\n", gpio); ++ // deb_debug("GPIO, initialised gpio->secure at %p\n", gpio->secure); ++ // deb_debug("GPIO, initialised gpio->base at %p\n", gpio->base); ++ // deb_debug("GPIO, initialised gpio->gte_regs at %p\n", gpio->gte_regs); + + preserve_tegrachip(gpio); -+ -+ /* this section is from copydriver branch -- not valid here -+ -+ // these ifdefs do not define host and guest kernel module code -+ // but common code in the stock 'tegra186-gpio' -- it is compiled if module is set in .config -+ -+ // we actually have two gpio chips -- this probe function will be called twice. -+ // copy set value to ready export -+ -+ if ( ! strcmp(gpio->gpio.label,"tegra234-gpio") ) { -+ gpio_ready += 1; -+ } -+ else if ( ! strcmp(gpio->gpio.label,"tegra234-gpio-aon") ) { -+ gpio_ready += 1; -+ } -+ else -+ pr_err("Can't match gpio chip label, in %s"); -+ if ( gpio_ready > 2 ) -+ pr_err("Found too many chips, in %s"); -+ -+ memcpy(&preset_gpio_local[n], gpio, sizeof(struct tegra_gpio)); -+ if ( gpio_ready == 2 ) -+ complete(&gpio_data_ready); -+ -+ deb_debug("GPIO preset_gpio %s exported in \n", gpio->gpio.label); -+ */ + #endif return 0; } @@ -965,7 +928,7 @@ index 5e57824b283e..e56a6b06f91e 100644 static int tegra_gpio_resume_early(struct device *dev) { struct tegra_gpio *gpio = dev_get_drvdata(dev); -@@ -1723,7 +2085,9 @@ static struct platform_driver tegra186_gpio_driver = { +@@ -1723,7 +2048,9 @@ static struct platform_driver tegra186_gpio_driver = { .probe = tegra186_gpio_probe, .remove = tegra186_gpio_remove, }; @@ -977,7 +940,7 @@ index 5e57824b283e..e56a6b06f91e 100644 MODULE_DESCRIPTION("NVIDIA Tegra186 GPIO controller driver"); MODULE_AUTHOR("Thierry Reding "); diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c -index 2613881a66e6..7c00c935c69f 100644 +index 2613881a66e6..1b052acf3753 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -83,6 +83,16 @@ struct linehandle_state { @@ -987,8 +950,8 @@ index 2613881a66e6..7c00c935c69f 100644 +// #define GPIO_DEBUG + +#ifdef GPIO_DEBUG -+#define deb_info(fmt, ...) printk(KERN_INFO"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) -+#define deb_debug(fmt, ...) printk(KERN_DEBUG"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) +#else +#define deb_info(fmt, ...) +#define deb_debug(fmt, ...) @@ -1176,7 +1139,7 @@ index 2613881a66e6..7c00c935c69f 100644 cdev_device_del(&gdev->chrdev, &gdev->dev); } diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c -index 30e2476a6dc4..873cd7ff1753 100644 +index 30e2476a6dc4..aab40f1cd52f 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -6,8 +6,21 @@ @@ -1186,8 +1149,8 @@ index 30e2476a6dc4..873cd7ff1753 100644 +// #define GPIO_DEBUG + +#ifdef GPIO_DEBUG -+#define deb_info(fmt, ...) printk(KERN_INFO"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) -+#define deb_debug(fmt, ...) printk(KERN_DEBUG"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) +#else +#define deb_info(fmt, ...) +#define deb_debug(fmt, ...) @@ -1229,7 +1192,7 @@ index 30e2476a6dc4..873cd7ff1753 100644 err = gpio_request_one(array->gpio, array->flags, array->label); if (err) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c -index 3ef71ca242ba..0aaefbe3f6d9 100644 +index 3ef71ca242ba..bf62aeee66a1 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -18,6 +18,16 @@ @@ -1239,8 +1202,8 @@ index 3ef71ca242ba..0aaefbe3f6d9 100644 +// #define GPIO_DEBUG + +#ifdef GPIO_DEBUG -+#define deb_info(fmt, ...) printk(KERN_INFO"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) -+#define deb_debug(fmt, ...) printk(KERN_DEBUG"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) +#else +#define deb_info(fmt, ...) +#define deb_debug(fmt, ...) @@ -1259,7 +1222,7 @@ index 3ef71ca242ba..0aaefbe3f6d9 100644 /* If buf is not a number then try to find by name */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c -index 50abb1c20df0..bdd7f6310dce 100644 +index 50abb1c20df0..9e466eeff297 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -31,6 +31,16 @@ @@ -1269,8 +1232,8 @@ index 50abb1c20df0..bdd7f6310dce 100644 +// #define GPIO_DEBUG + +#ifdef GPIO_DEBUG -+#define deb_info(fmt, ...) printk(KERN_INFO"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) -+#define deb_debug(fmt, ...) printk(KERN_DEBUG"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) +#else +#define deb_info(fmt, ...) +#define deb_debug(fmt, ...) @@ -2530,7 +2493,7 @@ index 50abb1c20df0..bdd7f6310dce 100644 -#endif /* DEBUG_FS */ + #endif /* DEBUG_FS */ diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c -index c73b34e03aae..b31c7f3cb6be 100644 +index c73b34e03aae..61fd6b71d8e0 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -26,16 +26,25 @@ @@ -2552,8 +2515,8 @@ index c73b34e03aae..b31c7f3cb6be 100644 +// #define GPIO_DEBUG + +#ifdef GPIO_DEBUG -+#define deb_info(fmt, ...) printk(KERN_INFO"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) -+#define deb_debug(fmt, ...) printk(KERN_DEBUG"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) +#else +#define deb_info(fmt, ...) +#define deb_debug(fmt, ...) @@ -3464,7 +3427,7 @@ index c73b34e03aae..b31c7f3cb6be 100644 pinctrl_init_debugfs(); return 0; diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c -index f9ecbebe6442..baf6079962f5 100644 +index f9ecbebe6442..f5541a687dc3 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -34,13 +34,28 @@ @@ -3474,8 +3437,8 @@ index f9ecbebe6442..baf6079962f5 100644 +// #define GPIO_DEBUG + +#ifdef GPIO_DEBUG -+#define deb_info(fmt, ...) printk(KERN_INFO"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) -+#define deb_debug(fmt, ...) printk(KERN_DEBUG"GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++#define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) +#else +#define deb_info(fmt, ...) +#define deb_debug(fmt, ...) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index d6f10a0b6..3a75f69c2 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -1,16 +1,3 @@ -diff --git a/drivers/Makefile b/drivers/Makefile -index d3500440b928..ea8c07ff3700 100644 ---- a/drivers/Makefile -+++ b/drivers/Makefile -@@ -191,3 +191,8 @@ obj-$(CONFIG_GNSS) += gnss/ - obj-$(CONFIG_INTERCONNECT) += interconnect/ - obj-$(CONFIG_COUNTER) += counter/ - obj-$(CONFIG_MOST) += most/ -+# -+# -+# -+obj-y += gpio-host-proxy/ -+obj-y += gpio-guest-proxy/ diff --git a/drivers/Kconfig b/drivers/Kconfig index dcecc9f6e33f..ed7d58d68ba6 100644 --- a/drivers/Kconfig @@ -26,6 +13,15 @@ index dcecc9f6e33f..ed7d58d68ba6 100644 +endif + endmenu +diff --git a/drivers/Makefile b/drivers/Makefile +--- a/drivers/Makefile 2024-02-23 12:43:23.937526730 +0000 ++++ b/drivers/Makefile 2024-05-02 07:08:17.247764422 +0000 +@@ -193,0 +194,5 @@ ++# ++# ++# ++obj-y += gpio-host-proxy/ ++obj-y += gpio-guest-proxy/ diff --git a/drivers/gpio-guest-proxy/Kconfig b/drivers/gpio-guest-proxy/Kconfig new file mode 100644 index 0000000..e25a19a @@ -52,10 +48,10 @@ index 0000000..2580e02 +obj-$(CONFIG_TEGRA_GPIO_GUEST_PROXY) += gpio-guest-proxy.o diff --git a/drivers/gpio-guest-proxy/gpio-guest-proxy.c b/drivers/gpio-guest-proxy/gpio-guest-proxy.c new file mode 100644 -index 0000000..8917c30 +index 0000000..95aa5be --- /dev/null +++ b/drivers/gpio-guest-proxy/gpio-guest-proxy.c -@@ -0,0 +1,759 @@ +@@ -0,0 +1,775 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -99,9 +95,9 @@ index 0000000..8917c30 +#define GPIO_DEBUG_VERBOSE // also activates deb_verbose commands + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\'" fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\'" fmt, __func__ , __FILE__, ##__VA_ARGS__) -+ #define deb_error(fmt, ...) printk(KERN_ERR "GPIO func \'%s\' in file \'%s\'" fmt, __func__ , __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__ , __FILE__, ##__VA_ARGS__) ++ #define deb_error(fmt, ...) printk(KERN_ERR "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__ , __FILE__, ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -111,6 +107,7 @@ index 0000000..8917c30 +#ifdef GPIO_DEBUG_VERBOSE + #define deb_verbose deb_debug + extern void hexDump ( ++ const char * deviceName, + const char * desc, + const void * addr, + const int len @@ -150,7 +147,7 @@ index 0000000..8917c30 + memcpy(io_buffer, msg, msg_len); + + #ifdef GPIO_DEBUG_VERBOSE -+ hexDump("msg", &msg, msg_len); ++ hexDump(DEVICE_NAME, "msg", &msg, msg_len); + deb_verbose("msg signal is: %c\n", *(char *)msg); + #endif + @@ -393,11 +390,22 @@ index 0000000..8917c30 + .write = write + }; + ++static bool is_set_up = false; ++ +/** + * Initializes module at installation + */ -+int tegra_gpio_guest_init(struct gpio_chip *gpio) ++int tegra_gpio_guest_init(void) +{ ++ // Note: gpio is not referenced, the init is agnostic to which chip triggered this function ++ // In an earlier versio of the code we stored the gpio struct pointer in a static table ++ // int this_chip_id = gpio.gpio->gpiodev.id; ++ // char this_device[12]; ++ if(is_set_up) { ++ deb_error("Attempting to set up guest driver twice\n"); ++ return -EPERM; ++ } ++ + deb_info("installing module."); + + deb_info("gpio_vpa: 0x%llx", gpio_vpa); @@ -406,6 +414,7 @@ index 0000000..8917c30 + pr_err("Failed, gpio_vpa not defined"); + } + ++ + // Allocate a major number for the device. + major_number = register_chrdev(0, DEVICE_NAME, &fops); + if (major_number < 0) @@ -449,6 +458,7 @@ index 0000000..8917c30 + // gpio_hook is called by preserve_tegrachip() in gpio_tegra186.c -- don't call it here + // gpio_hook() + ++ is_set_up = true; + return 0; +} + @@ -475,6 +485,8 @@ index 0000000..8917c30 + unregister_chrdev(major_number, DEVICE_NAME); // unregister the major number + deb_info("Goodbye from the LKM!\n"); + unregister_chrdev(major_number, DEVICE_NAME); ++ ++ is_set_up = false; + return; +} + @@ -577,7 +589,7 @@ index 0000000..8917c30 + } + + // print copied user parameters -+ hexDump ("Chardev input", kbuf, len); ++ hexDump (DEVICE_NAME, "Chardev input", kbuf, len); + + + // make gpio-host type call to gpio @@ -840,10 +852,10 @@ index 0000000..c2e0184 +obj-$(CONFIG_TEGRA_GPIO_HOST_PROXY) += gpio-host-proxy.o diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.c b/drivers/gpio-host-proxy/gpio-host-proxy.c new file mode 100644 -index 0000000..aeee8cf +index 0000000..361804e --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.c -@@ -0,0 +1,646 @@ +@@ -0,0 +1,645 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -879,8 +891,8 @@ index 0000000..aeee8cf +#define GPIO_DEBUG_VERBOSE // also activates deb_verbose commands + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\'" fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\'" fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -930,12 +942,14 @@ index 0000000..aeee8cf + +#ifdef GPIO_DEBUG_VERBOSE + // Usage: -+ // hexDump(desc, addr, len, perLine); ++ // hexDump(devName, desc, addr, len, perLine); ++ // devName name of device being debugged, for reference + // desc: if non-NULL, printed as a description before hex dump. + // addr: the address to start dumping from. + // len: the number of bytes to dump. + // perLine: number of bytes on each output line. + void hexDump ( ++ const char * deviceName, + const char * desc, + const void * addr, + const int len @@ -957,16 +971,16 @@ index 0000000..aeee8cf + // Length checks. + + if (len == 0) { -+ printk(DEVICE_NAME ": ZERO LENGTH\n"); ++ printk("%s: ZERO LENGTH\n", deviceName); + return; + } + if (len < 0) { -+ printk(DEVICE_NAME ": NEGATIVE LENGTH: %d\n", len); ++ printk("%s: NEGATIVE LENGTH: %d\n", deviceName, len); + return; + } + + if(len > 400){ -+ printk(DEVICE_NAME ": VERY LONG: %d\n", len); ++ printk("%s: VERY LONG: %d\n", deviceName, len); + return; + } + @@ -1010,7 +1024,7 @@ index 0000000..aeee8cf + + p_out_buff += sprintf (p_out_buff, " %s\n", buff); + -+ printk(DEVICE_NAME ": %s", out_buff); ++ printk("%s: %s", deviceName, out_buff); + } + EXPORT_SYMBOL_GPL(hexDump); +#else @@ -1022,41 +1036,41 @@ index 0000000..aeee8cf + */ +static int gpio_host_proxy_probe(struct platform_device *pdev) +{ -+// int i; ++ // int i; + deb_info("installing module.\n"); + -+// ********************* -+// start of TODO clocks and resets -- this commect section is probably not valid -+ -+// // Read allowed clocks and reset from the device tree -+// // if clocks or resets are not defined, not initialize the module -+// gpio_ares.clocks_size = of_property_read_variable_u32_array(pdev->dev.of_node, -+// "allowed-clocks", gpio_ares.clock, 0, GPIO_HOST_MAX_CLOCKS_SIZE); -+// -+// if(gpio_ares.clocks_size <= 0){ -+// pr_err("No allowed clocks defined\n"); -+// return EINVAL; -+// } -+// -+// deb_info("gpio_ares.clocks_size: %d", gpio_ares.clocks_size); -+// for (i = 0; i < gpio_ares.clocks_size; i++) { -+// deb_info("gpio_ares.clock %d", gpio_ares.clock[i]); -+// } -+// -+// gpio_ares.resets_size = of_property_read_variable_u32_array(pdev->dev.of_node, -+// "allowed-resets", gpio_ares.reset, 0, GPIO_HOST_MAX_RESETS_SIZE); -+// -+// if(gpio_ares.resets_size <= 0){ -+// pr_err("No allowed resets defined\n"); -+// return EINVAL; -+// } -+// -+// deb_info("gpio_ares.resets_size: %d", gpio_ares.resets_size); -+// for (i = 0; i < gpio_ares.resets_size; i++) { -+// deb_info("gpio_ares.reset %d", gpio_ares.reset[i]); -+// } -+// end of TODO clocks and resets -+// ********************* ++ // ********************* ++ // start of TODO clocks and resets -- this commect section is probably not valid ++ ++ // // Read allowed clocks and reset from the device tree ++ // // if clocks or resets are not defined, not initialize the module ++ // gpio_ares.clocks_size = of_property_read_variable_u32_array(pdev->dev.of_node, ++ // "allowed-clocks", gpio_ares.clock, 0, GPIO_HOST_MAX_CLOCKS_SIZE); ++ // ++ // if(gpio_ares.clocks_size <= 0){ ++ // pr_err("No allowed clocks defined\n"); ++ // return EINVAL; ++ // } ++ // ++ // deb_info("gpio_ares.clocks_size: %d", gpio_ares.clocks_size); ++ // for (i = 0; i < gpio_ares.clocks_size; i++) { ++ // deb_info("gpio_ares.clock %d", gpio_ares.clock[i]); ++ // } ++ // ++ // gpio_ares.resets_size = of_property_read_variable_u32_array(pdev->dev.of_node, ++ // "allowed-resets", gpio_ares.reset, 0, GPIO_HOST_MAX_RESETS_SIZE); ++ // ++ // if(gpio_ares.resets_size <= 0){ ++ // pr_err("No allowed resets defined\n"); ++ // return EINVAL; ++ // } ++ // ++ // deb_info("gpio_ares.resets_size: %d", gpio_ares.resets_size); ++ // for (i = 0; i < gpio_ares.resets_size; i++) { ++ // deb_info("gpio_ares.reset %d", gpio_ares.reset[i]); ++ // } ++ // end of TODO clocks and resets ++ // ********************* + + // Allocate a major number for the device. + major_number = register_chrdev(0, DEVICE_NAME, &fops); @@ -1209,8 +1223,7 @@ index 0000000..aeee8cf + } + + // print copied user parameters -+ hexDump ("Chardev input", kbuf, len); -+ ++ hexDump (DEVICE_NAME, "Chardev input", kbuf, len); + + // make gpio-host type call to gpio + deb_verbose("enter switch with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); @@ -1466,8 +1479,6 @@ index 0000000..aeee8cf +}; +// builtin_platform_driver(gpio_host_proxy_driver); + -+ -+// TODO do not initialise host proxy driver in guest. +static int __init gpio_host_proxy_init(void) +{ + int ret = 0; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix index ac073d6cd..6b288952c 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix @@ -39,9 +39,9 @@ in { hardware.deviceTree = { # Enable hardware.deviceTree for handle host dtb overlays enable = true; - # name = builtin.trace "Debug dtb: tegra234-p3701-0000-p3737-0000.dtb" "tegra234-p3701-0000-p3737-0000.dtb"; - # name = pkgs.builtin.trace "Debug dtb: tegra234-p3701-host-passthrough.dtb" "tegra234-p3701-host-passthrough.dtb"; - name = "tegra234-p3701-host-passthrough.dtb"; + name = builtins.trace "Debug dtb name (gpio-virt-host): tegra234-p3701-0000-p3737-0000.dtb" "tegra234-p3701-0000-p3737-0000.dtb"; + # name = builtins.trace "Debug dtb name (gpio-virt-host): tegra234-p3701-host-passthrough.dtb" "tegra234-p3701-host-passthrough.dtb"; + # name = "tegra234-p3701-host-passthrough.dtb"; # using overlay file: overlays = [ @@ -50,9 +50,9 @@ in { dtsFile = ./gpio_pt_host_overlay.dtso; # Apply overlay only to host passthrough device tree - # filter = builtin.trace "Debug dtb: tegra234-p3701-0000-p3737-0000.dtb" "tegra234-p3701-0000-p3737-0000.dtb"; - # filter = pkgs.builtin.trace "Debug dtb: tegra234-p3701-host-passthrough.dtb" "tegra234-p3701-host-passthrough.dtb"; - filter = "tegra234-p3701-host-passthrough.dtb"; + filter = builtins.trace "Debug dtb filter (gpio-virt-host): tegra234-p3701-0000-p3737-0000.dtb" "tegra234-p3701-0000-p3737-0000.dtb"; + # filter = builtins.trace "Debug dtb filter (gpio-virt-host): tegra234-p3701-host-passthrough.dtb" "tegra234-p3701-host-passthrough.dtb"; + # filter = "tegra234-p3701-host-passthrough.dtb"; } ]; }; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/default.nix index 53c5ecd5e..c77905e8e 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/default.nix @@ -21,7 +21,7 @@ in { ''; ghaf.hardware.nvidia.virtualization.enable = true; - ghaf.virtualization.microvm.netvm.extraModules = builtins.trace "Setting, ghaf.virtualization.microvm.netvm.extraModules" [ + ghaf.virtualization.microvm.netvm.extraModules = builtins.trace "NetVM: setting ghaf.virtualization.microvm.netvm.extraModules" [ { # Use serial passthrough (ttyAMA0) and virtual PCI serial (ttyS0) # as Linux console @@ -74,12 +74,12 @@ in { # Apply the device tree overlay only to tegra234-p3701-host-passthrough.dtb hardware.deviceTree.overlays = [ { - name = "uarti_pt_host_overlay"; + name = builtins.trace "Debug dtb name (uarti-net-vm): uarti_pt_host_overlay" "uarti_pt_host_overlay"; dtsFile = ./uarti_pt_host_overlay.dts; # Apply overlay only to host passthrough device tree # TODO: make this avaliable if PCI passthrough is disabled - filter = "tegra234-p3701-host-passthrough.dtb"; + filter = builtins.trace "Debug dtb filter (uarti-net-vm): tegra234-p3701-host-passthrough.dtb" "tegra234-p3701-host-passthrough.dtb"; } ]; }; diff --git a/modules/microvm/virtualization/microvm/appvm.nix b/modules/microvm/virtualization/microvm/appvm.nix index 6aa265d2d..2481666f6 100644 --- a/modules/microvm/virtualization/microvm/appvm.nix +++ b/modules/microvm/virtualization/microvm/appvm.nix @@ -103,22 +103,12 @@ ]; writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; - qemu = { - extraArgs = [ - "-M" - "accel=kvm:tcg,mem-merge=on,sata=off" - "-device" - "vhost-vsock-pci,guest-cid=${toString cid}" - ]; - - machine = - { - # Use the same machine type as the host - x86_64-linux = "q35"; - aarch64-linux = "virt"; - } - .${configHost.nixpkgs.hostPlatform.system}; - }; + qemu.extraArgs = [ + "-M" + "q35,accel=kvm:tcg,mem-merge=on,sata=off" + "-device" + "vhost-vsock-pci,guest-cid=${toString cid}" + ]; }; fileSystems."/run/waypipe-ssh-public-key".options = ["ro"]; diff --git a/modules/microvm/virtualization/microvm/gpiovm.nix b/modules/microvm/virtualization/microvm/gpiovm.nix index a786d1295..eee981865 100644 --- a/modules/microvm/virtualization/microvm/gpiovm.nix +++ b/modules/microvm/virtualization/microvm/gpiovm.nix @@ -56,6 +56,7 @@ graphics.enable= false; qemu = { + /* tmp removed for GPIO testing machine = { # Use the same machine type as the host @@ -63,25 +64,26 @@ aarch64-linux ="virt"; } .${configHost.nixpkgs.hostPlatform.system}; + */ serialConsole = true; - extraArgs = [ - # "-dtb ${gpioGuestDtbName}" - # "-monitor chardev=mon0,mode=readline" - # "-no-reboot" + extraArgs = builtins.trace "GpioVM: Evaluating QEMU parameters for gpio-vm" [ + "-dtb" "${gpioGuestDtbName}" + # "-serial" "/dev/tty10" # Could not open '/dev/tty10': Permission denied ]; /* - extraArgs = builtins.trace "Evaluating qemu.extraArgs for gpio-vm" [ + extraArgs = builtins.trace "GpioVM: Evaluating qemu.extraArgs for gpio-vm" [ # Add custom dtb to Gpio-VM with VDA "-dtb ${gpioGuestDtbName}" "-monitor chardev=mon0,mode=readline" "-no-reboot" # "-drive file=${tmp_rootfs},if=virtio,format=qcow2" # -nographic \ - # -machine virt,accel=kvm \ + # -machine virt,accel=kvm \q # -cpu host \ # -m 4G \ # -smp 2 \ # -kernel ${kernel} \ + # "-monitor" "chardev=ttyTHS2,mode=readline" ]; */ }; diff --git a/modules/microvm/virtualization/microvm/guivm.nix b/modules/microvm/virtualization/microvm/guivm.nix index 8dee3cdde..b8200e874 100644 --- a/modules/microvm/virtualization/microvm/guivm.nix +++ b/modules/microvm/virtualization/microvm/guivm.nix @@ -94,20 +94,10 @@ ]; writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; - qemu = { - extraArgs = [ - "-device" - "vhost-vsock-pci,guest-cid=${toString cfg.vsockCID}" - ]; - - machine = - { - # Use the same machine type as the host - x86_64-linux = "q35"; - aarch64-linux = "virt"; - } - .${configHost.nixpkgs.hostPlatform.system}; - }; + qemu.extraArgs = [ + "-device" + "vhost-vsock-pci,guest-cid=${toString cfg.vsockCID}" + ]; }; imports = [ diff --git a/modules/microvm/virtualization/microvm/netvm.nix b/modules/microvm/virtualization/microvm/netvm.nix index f3e513b0d..bb47d575c 100644 --- a/modules/microvm/virtualization/microvm/netvm.nix +++ b/modules/microvm/virtualization/microvm/netvm.nix @@ -35,6 +35,8 @@ nixpkgs.buildPlatform.system = configHost.nixpkgs.buildPlatform.system; nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system; + microvm.hypervisor = "qemu"; + networking = { firewall.allowedTCPPorts = [53]; firewall.allowedUDPPorts = [53]; @@ -86,7 +88,6 @@ microvm = { optimize.enable = true; - hypervisor = "qemu"; shares = [ { tag = "ro-store"; @@ -95,15 +96,6 @@ } ]; writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; - qemu = { - machine = - { - # Use the same machine type as the host - x86_64-linux = "q35"; - aarch64-linux = "virt"; - } - .${configHost.nixpkgs.hostPlatform.system}; - }; }; imports = [../../../common]; diff --git a/modules/microvm/virtualization/microvm/qemu-gpio-guestvm.dtb b/modules/microvm/virtualization/microvm/qemu-gpio-guestvm.dtb index 00f21a374ed60ec9ab29300f7d27ea7f39c497f6..9ae873329d31ccde68f9f7eae35821bf5da256a3 100644 GIT binary patch delta 1903 zcmbW2J8Tm{5I}bi5JU=5Y$=XoLNGXxKzt-|%qN(SfrS50G?5Sm$N8{NIiJnlnO}jC zXi%UbD*>VjLPClt2#A7T2NWrgKop3Af(9uRQ1B5nd}i+B3^YiLG`nwh=Vo{3=Jq~j zJ`HxhnEUesV~aj9##S>He*t|bXb7|s6hvAC<5%u6z;A^>3 zkx(!$h_uk_eH&JmvM_QU6okj2!7YPu^b8pCR;?KcGSN*FAXavp+}kiX4KRX{ARpi z*SAF+3`eW7QZmUPuJq5(ryfhol}5L%@Ai8CM!}8XLjLE2*G^CyjMN6h+Nk*X1Ujm_|~{p;xUm8MZ!D|9u6pahT-8ZQ3z#1 z6O5g~z537dUU54Tk@!0CEYvagkIjyTcZu2mV5lEh{n3WZy0;p&4uaq~?(ZEp@lL4OU&Wz$EB8xMciHIa?alUM&5<8T9FE^`VL-cFY=8b` zqV84f+6`47-EQ9>d-dhf-uHE5gF7-wKODR9){-ZmuO1as^RA~8c%93ZV&*kj z)eOUCwxc)|o0SWSnKLwYOt+khV#sBMSz2B;m7*pdwzeJ6O-Hk=O4*UslId6_!_X|2 wAfs-u>FBomrWz%6*o|v@ET&o$qILcnF+Bh5Z>S0HumAu6 delta 691 zcmez5`p#P80`I@K3=FdG7#J8V7#O6#0BH>%76f7eAO-@)UqJCa8#QhSOtuiS(_)y$ zz|g<~6lVs@LTC_w4iJ|>#f5|kMFVE_a`A($f=)=oYm zt1Va#6k&vlf|yN{R=am%Y=j5aoP0mwl0RXOyn=$|Z diff --git a/modules/microvm/virtualization/microvm/simple-chardev-test.sh b/modules/microvm/virtualization/microvm/simple-chardev-test.sh index e0082eb3c..33f121493 100755 --- a/modules/microvm/virtualization/microvm/simple-chardev-test.sh +++ b/modules/microvm/virtualization/microvm/simple-chardev-test.sh @@ -1,7 +1,3 @@ -while true -do - sleep 10 - # For pin names look at gpio_40pin_header.png # some examples # GPIO09 PBB.00 - gpiochip 1, offset 8/0x08 @@ -12,30 +8,38 @@ do # chipnum='\x00' # tegra234-gpio # chipnum='\x01' # tegra234-gpio-aon - # lvl0='\x00' - # lvl1='\x01' + lvl0='\x00' + lvl1='\x01' n_a='\x00' - offset='\x55' # PN.01/GPIO27 # line offset in hex + offset='\x55' # example: PN.01/GPIO27 has line offset hex '\x55' pad='\x00\x00\x00\x00' + # chardev='/dev/gpio-host' + chardev='/dev/gpio-guest' function res { signal='r' # reserve line - echo -n -e ${signal}${chipnum}${n_a}${offset}${pad} >/dev/gpio-host + echo -n -e ${signal}${chipnum}${n_a}${offset}${pad} >> ${chardev} signal='o' # set pin as output - echo -n -e ${signal}${chipnum}${lvl1}${offset}${pad} >/dev/gpio-host + # this will also set a level + echo -n -e ${signal}${chipnum}${lvl1}${offset}${pad} >> ${chardev} } function setlevel { - echo -n -e ${signal}${chipnum}${level}${offset}${pad} >/dev/gpio-host + echo -n -e ${signal}${chipnum}${level}${offset}${pad} >> ${chardev} sleep 0.0050 } function free { signal='f' # free line - echo -n -e ${signal}${chipnum}${n_a}${offset}${pad} >/dev/gpio-host + echo -n -e ${signal}${chipnum}${n_a}${offset}${pad} >> ${chardev} } +while true +do + sleep 10 + echo -n '.' + chipnum='\x01';offset='\x08'; res chipnum='\x01';offset='\x09'; res chipnum='\x00';offset='\x2B'; res diff --git a/targets/nvidia-jetson-orin/flake-module.nix b/targets/nvidia-jetson-orin/flake-module.nix index c1edb54b4..b95d0dd18 100644 --- a/targets/nvidia-jetson-orin/flake-module.nix +++ b/targets/nvidia-jetson-orin/flake-module.nix @@ -59,9 +59,10 @@ hardware.nvidia.orin = { enable = true; somType = som; - agx.enableNetvmWlanPCIPassthrough = som == "agx"; - # agx.enableNetvmWlanPCIPassthrough = false; - nx.enableNetvmEthernetPCIPassthrough = som == "nx"; + # agx.enableNetvmWlanPCIPassthrough = som == "agx"; + agx.enableNetvmWlanPCIPassthrough = false; + # nx.enableNetvmEthernetPCIPassthrough = som == "nx"; + nx.enableNetvmEthernetPCIPassthrough = false; agx.enableGPIOPassthrough = som == "agx"; }; @@ -69,8 +70,8 @@ virtualization.enable = true; virtualization.host.bpmp.enable = false; passthroughs.host.uarta.enable = false; - passthroughs.uarti_net_vm.enable = som == "agx"; - # passthroughs.uarti_net_vm.enable = false; + # passthroughs.uarti_net_vm.enable = som == "agx"; + passthroughs.uarti_net_vm.enable = false; virtualization.host.gpio.enable = som == "agx"; }; @@ -84,7 +85,7 @@ virtualization.microvm.netvm.extraModules = netvmExtraModules; virtualization.microvm.gpiovm.enable = true; - # virtualization.microvm.gpiovm.extraModules = gpiovmExtraModules; + virtualization.microvm.gpiovm.extraModules = gpiovmExtraModules; # Enable all the default UI applications profiles = { diff --git a/targets/nvidia-jetson-orin/optee.nix b/targets/nvidia-jetson-orin/optee.nix index 34af16319..b12903761 100644 --- a/targets/nvidia-jetson-orin/optee.nix +++ b/targets/nvidia-jetson-orin/optee.nix @@ -17,7 +17,7 @@ opteeSource = pkgs.fetchgit { url = "https://nv-tegra.nvidia.com/r/tegra/optee-src/nv-optee"; rev = "jetson_${l4tVersion}"; - sha256 = "sha256-jJOMig2+9FlKA9gJUCH/dva7ZtAq1typZSNGKyM7tlg="; + sha256 = "sha256-44RBXFNUlqZoq3OY/OFwhiU4Qxi4xQNmetFmlrr6jzY="; }; opteeXtest = stdenv.mkDerivation { @@ -120,8 +120,6 @@ "ffd2bded-ab7d-4988-95ee-e4962fff7154.ta" "b3091a65-9751-4784-abf7-0298a7cc35ba.ta" "f157cda0-550c-11e5-a6fa-0002a5d5c51b.ta" - "5c206987-16a3-59cc-ab0f-64b9cfc9e758.ta" - "a720ccbb-51da-417d-b82e-e5445d474a7a.ta" ]; pkcs11TaPath = { name = "fd02c9da-306c-48c7-a49c-bbd827ae86ee.ta"; From cbee01b04dc2fb4640289af98fecc72c24142bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Thu, 2 May 2024 09:34:48 +0000 Subject: [PATCH 15/26] fixup -- guest driver does not work --- .../gpio-virt-common/patches/0004-gpio-virt-drivers.patch | 2 +- modules/microvm/virtualization/microvm/gpio-test.nix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index 3a75f69c2..92f352a64 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -15,7 +15,7 @@ index dcecc9f6e33f..ed7d58d68ba6 100644 endmenu diff --git a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile 2024-02-23 12:43:23.937526730 +0000 -+++ b/drivers/Makefile 2024-05-02 07:08:17.247764422 +0000 ++++ b/drivers/Makefile 2024-05-02 09:33:53.552379508 +0000 @@ -193,0 +194,5 @@ +# +# diff --git a/modules/microvm/virtualization/microvm/gpio-test.nix b/modules/microvm/virtualization/microvm/gpio-test.nix index 23f125026..993459cd8 100644 --- a/modules/microvm/virtualization/microvm/gpio-test.nix +++ b/modules/microvm/virtualization/microvm/gpio-test.nix @@ -4,7 +4,7 @@ let scriptPath = pkgs.concatTextFile { name = "gpio-test-script"; - files = [ "./simple-chardev.test.sh" ]; + files = [ "./simple-chardev-test.sh" ]; executable = true; destination = "/bin/gpio-test-script"; }; From 607fc1f04f1f0440009e0b65c6b97ce43941bd49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Mon, 6 May 2024 12:03:57 +0000 Subject: [PATCH 16/26] Towards working guest driver for GPIO passthrough GPIO guest driver in VM throws error at initialisation of shared qemu memory. ioremap fails: mem_iova = ioremap(gpio_vpa, MEM_SIZE); --- mk_patches.sh | 7 +- .../patches/0003-gpio-virt-kernel.patch | 171 ++++++++++++------ .../patches/0004-gpio-virt-drivers.patch | 8 +- .../virtualization/overlays/default.nix | 2 +- .../microvm/qemu-gpio-guestvm.dtb | Bin 8946 -> 8911 bytes 5 files changed, 130 insertions(+), 58 deletions(-) diff --git a/mk_patches.sh b/mk_patches.sh index 08ead6290..cc641cec1 100755 --- a/mk_patches.sh +++ b/mk_patches.sh @@ -1,3 +1,8 @@ +if [ $(whoami) == "root" ]; then + echo "Please, do not run as root -- dir paths will fail" + exit 1 +fi + home="/home/$(id -un)" # sw=${PWD} sw="${home}/software" @@ -107,7 +112,7 @@ git -C kernel-5.10/ diff basepoint -- "arch/arm64/configs/defconfig" \ # build ghaf cd ${ghaf} -./build.sh +./build.sh $@ # ------ diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch index 823b3c631..9c3e84c51 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch @@ -247,7 +247,7 @@ index f66fc17faee4..629bc13aa4c3 100644 } subsys_initcall(tegra_gpio_init); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c -index 5e57824b283e..581ba1f606d8 100644 +index 5e57824b283e..1db1dc2d2273 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -44,7 +44,7 @@ @@ -282,7 +282,7 @@ index 5e57824b283e..581ba1f606d8 100644 + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) + #endif -+ ++ + #ifdef GPIO_DEBUG_VERBOSE + #define deb_verbose deb_debug + #else @@ -624,7 +624,7 @@ index 5e57824b283e..581ba1f606d8 100644 for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; unsigned int offset, p = port->port; -@@ -1107,6 +1211,188 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +@@ -1107,6 +1211,190 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) return -EINVAL; } @@ -672,6 +672,7 @@ index 5e57824b283e..581ba1f606d8 100644 + extern int tegra186_gpio_add_pin_ranges_redirect(struct gpio_chip *chip); + + static inline void gpio_hook(struct tegra_gpio *gpio) { ++ deb_debug("Entering function with pointer: %p and gpio: %s", gpio, gpio->gpio.label); + gpio->gpio.request = gpiochip_generic_request_redirect; + gpio->gpio.free = gpiochip_generic_free_redirect; + gpio->gpio.get_direction = tegra186_gpio_get_direction_redirect; @@ -692,6 +693,7 @@ index 5e57824b283e..581ba1f606d8 100644 + +// this function sets the standard bindings used by the host driver +static inline void gpio_unhook(struct tegra_gpio *gpio) { ++ deb_debug("Entering function with pointer: %p and gpio: %s", gpio, gpio->gpio.label); + gpio->gpio.request = gpiochip_generic_request; + gpio->gpio.free = gpiochip_generic_free; + gpio->gpio.get_direction = tegra186_gpio_get_direction; @@ -776,17 +778,17 @@ index 5e57824b283e..581ba1f606d8 100644 + void unpreserve_all_tegrachips(void) { + struct tegra_gpio ** tegrachip; + int i = 0; -+ ++ + // wait until tegra driver has set the tegra_gpio_hosts array -- this seems a bit paranoid + while (atomic_read(&tegra_gpio_hosts_ready) != MAX_CHIP) { + msleep(100); // Sleep briefly instead of looping infinitely. + if( i++ > 120 ) { -+ pr_err("could not access tegra_gpio chip array\n"); ++ pr_err("GPIO could not access tegra_gpio chip array\n"); + return; + } + } + for( i = 0 ; i < MAX_CHIP ; i++){ -+ tegrachip = &tegra_gpio_hosts[i]; ++ tegrachip = &tegra_gpio_hosts[i]; + if(*tegrachip) + gpio_unhook(*tegrachip); + *tegrachip = NULL; @@ -801,7 +803,7 @@ index 5e57824b283e..581ba1f606d8 100644 + while (atomic_read(&tegra_gpio_hosts_ready) != MAX_CHIP) { + msleep(100); // Sleep briefly instead of looping infinitely. + if( i++ > 120 ) { -+ pr_err("tegra_gpio_hosts setup error\n"); ++ pr_err("GPIO tegra_gpio_hosts setup error\n"); + return NULL; + } + } @@ -813,12 +815,13 @@ index 5e57824b283e..581ba1f606d8 100644 static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; -@@ -1120,17 +1406,26 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1120,17 +1408,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) int value; void __iomem *base; ++ bool kernel_is_on_guest = false; + static bool guest_proxy_is_set_up = false; -+ ++ + deb_debug("Probing gpio\n"); + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); @@ -827,6 +830,18 @@ index 5e57824b283e..581ba1f606d8 100644 + pr_err("GPIO devm_kzalloc error"); return -ENOMEM; + } ++ ++ #if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++ ++ deb_debug("GPIO Proxy code\n"); ++ ++ // If virtual-pa node is defined, it means that we are using a virtual GPIO ++ err = of_property_read_u64(pdev->dev.of_node, "virtual-pa", &gpio_vpa); ++ if(!err) { ++ deb_info("GPIO kernel is on guest, virtual-pa: 0x%llx\n", gpio_vpa); ++ kernel_is_on_guest = true; ++ } ++ #endif gpio->soc = of_device_get_match_data(&pdev->dev); gpio->gpio.label = gpio->soc->name; @@ -835,14 +850,59 @@ index 5e57824b283e..581ba1f606d8 100644 gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security"); - if (IS_ERR(gpio->secure)) + if (IS_ERR(gpio->secure)) { -+ pr_err("GPIO *ERROR* devm_platform_ioremap_resource_byname pdev->name = \"%s\"", pdev->name); ++ pr_err("GPIO *ERROR* devm_platform_ioremap_resource_byname pdev->name=\"%s\"", pdev->name); return PTR_ERR(gpio->secure); + } + ++ deb_debug("num of ports = %d\n", gpio->soc->num_ports); /* count the number of banks in the controller */ for (i = 0; i < gpio->soc->num_ports; i++) -@@ -1194,21 +1489,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1139,6 +1450,13 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + + gpio->num_banks++; + ++ #if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++ // in we are on guest VM ++ // if(kernel_is_on_guest) ++ if(false) ++ goto quick_end_without_irq_setup; ++ #endif ++ + gpio->base = devm_platform_ioremap_resource_byname(pdev, "gpio"); + if (IS_ERR(gpio->base)) + return PTR_ERR(gpio->base); +@@ -1172,14 +1490,18 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + } + + err = platform_irq_count(pdev); +- if (err < 0) ++ if (err < 0) { ++ pr_err("GPIO platform_irq_count\n"); + return err; ++ } + + gpio->num_irq = err; + + err = tegra186_gpio_irqs_per_bank(gpio); +- if (err < 0) ++ if (err < 0) { ++ pr_err("GPIO tegra186_gpio_irqs_per_bank\n"); + return err; ++ } + + gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), + GFP_KERNEL); +@@ -1188,27 +1510,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + + for (i = 0; i < gpio->num_irq; i++) { + err = platform_get_irq(pdev, i); +- if (err < 0) ++ if (err < 0) { ++ pr_err("GPIO platform_get_irq"); + return err; ++ } + gpio->irq[i] = err; } @@ -861,66 +921,73 @@ index 5e57824b283e..581ba1f606d8 100644 - gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; - - gpio->gpio.base = -1; -+ #if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++ // gpio function pionters were set here in stock code, now moved to the end of this function + + for (i = 0; i < gpio->soc->num_ports; i++) + gpio->gpio.ngpio += gpio->soc->ports[i].pins; +@@ -1229,6 +1539,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + return -ENOMEM; + + names[offset + j] = name; ++ // deb_verbose("GPIO, name=\n", name); + } + + offset += port->pins; +@@ -1315,9 +1626,11 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + + platform_set_drvdata(pdev, gpio); + +- err = devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio); +- if (err < 0) +- return err; ++ err = devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio); ++ if (err < 0) { ++ pr_err("GPIO devm_gpiochip_add_data\n"); ++ return err; ++ } + + if (gpio->soc->is_hw_ts_sup) { + for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) { +@@ -1343,10 +1656,44 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + if (gpio->use_timestamp) + tegra_gte_setup(gpio); + +- return 0; ++ #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) ++ quick_end_without_irq_setup: + + deb_debug("GPIO Proxy code\n"); + -+ // If virtual-pa node is defined, it means that we are using a virtual GPIO -+ // then we have to initialise the gpio-guest -+ err = of_property_read_u64(pdev->dev.of_node, "virtual-pa", &gpio_vpa); -+ // code behind 'if' is executed in guest VM based on Device Tree parsing of virtual-pa above -+ if(!err) { -+ deb_info("GPIO virtual-pa: 0x%llx\n", gpio_vpa); ++ if( kernel_is_on_guest ) { + if( ! guest_proxy_is_set_up ) { -+ // we want to avoid double initialisation of tegra_gpio_guest_init() ++ deb_debug("A"); + ret = tegra_gpio_guest_init(); + guest_proxy_is_set_up = true; ++ deb_debug("B"); + } -+ // hook for all instances of "host" (i.e. default) driver in guest VM" ++ // hook for all function pointers in default driver in guest VM" ++ // guest should use gpio_hook() for its "host" driver ++ deb_debug("C"); + gpio_hook(gpio); ++ deb_debug("D"); + } + // error in reading virtual-pa is non fatal, it only means we are on host. + else { + // gpio_unhook is the same as standard settings + // unhooked pointers are for the host driver on host only -+ // guest should use gpio_hook() for its host driver + BUG_ON(gpio_vpa != 0); // assert we do not set up the vpa driver + gpio_unhook(gpio); + } + gpio->gpio.base = -1; ++ deb_debug("gpio function pointers are set for gpio label=%s\n", gpio->gpio.label); ++ preserve_tegrachip(gpio); + #else -+ deb_debug("Setting standard gpio functions in a non-proxy compile of driver\n") -+ BUG_ON(gpio_vpa != 0); // assert we do not set up the vpa driver ++ // this code segment is for standard operation in a compile without PROXY configuraton ++ deb_debug("Setting standard gpio functions for a non-proxy compile of driver\n") ++ BUG_ON(gpio_vpa != 0); // assert we do not set up the vpa driver, because non-proxy + gpio_unhook(gpio); + #endif -+ -+ deb_debug("gpio functions are set\n"); - - for (i = 0; i < gpio->soc->num_ports; i++) - gpio->gpio.ngpio += gpio->soc->ports[i].pins; -@@ -1229,6 +1543,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) - return -ENOMEM; - - names[offset + j] = name; -+ // deb_verbose("GPIO, name=\n", name); - } - - offset += port->pins; -@@ -1343,10 +1658,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) - if (gpio->use_timestamp) - tegra_gte_setup(gpio); - -+ #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) -+ -+ deb_debug("GPIO, initialised gpio label=%s\n", gpio->gpio.label); -+ // deb_debug("GPIO, initialised gpio at %p\n", gpio); -+ // deb_debug("GPIO, initialised gpio->secure at %p\n", gpio->secure); -+ // deb_debug("GPIO, initialised gpio->base at %p\n", gpio->base); -+ // deb_debug("GPIO, initialised gpio->gte_regs at %p\n", gpio->gte_regs); -+ -+ preserve_tegrachip(gpio); -+ #endif - return 0; ++ return 0; } -#ifdef CONFIG_PM_SLEEP @@ -928,7 +995,7 @@ index 5e57824b283e..581ba1f606d8 100644 static int tegra_gpio_resume_early(struct device *dev) { struct tegra_gpio *gpio = dev_get_drvdata(dev); -@@ -1723,7 +2048,9 @@ static struct platform_driver tegra186_gpio_driver = { +@@ -1723,7 +2070,9 @@ static struct platform_driver tegra186_gpio_driver = { .probe = tegra186_gpio_probe, .remove = tegra186_gpio_remove, }; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index 92f352a64..fa5728b26 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -14,8 +14,8 @@ index dcecc9f6e33f..ed7d58d68ba6 100644 + endmenu diff --git a/drivers/Makefile b/drivers/Makefile ---- a/drivers/Makefile 2024-02-23 12:43:23.937526730 +0000 -+++ b/drivers/Makefile 2024-05-02 09:33:53.552379508 +0000 +--- a/drivers/Makefile 2024-05-02 12:06:58.097355696 +0000 ++++ b/drivers/Makefile 2024-05-05 13:45:51.584426651 +0000 @@ -193,0 +194,5 @@ +# +# @@ -48,7 +48,7 @@ index 0000000..2580e02 +obj-$(CONFIG_TEGRA_GPIO_GUEST_PROXY) += gpio-guest-proxy.o diff --git a/drivers/gpio-guest-proxy/gpio-guest-proxy.c b/drivers/gpio-guest-proxy/gpio-guest-proxy.c new file mode 100644 -index 0000000..95aa5be +index 0000000..b42c834 --- /dev/null +++ b/drivers/gpio-guest-proxy/gpio-guest-proxy.c @@ -0,0 +1,775 @@ @@ -453,7 +453,7 @@ index 0000000..95aa5be + return -ENOMEM; + } + -+ deb_info("gpio_vpa: 0x%llX, mem_iova: %p\n", gpio_vpa, mem_iova); ++ deb_info("gpio_vpa: 0x%llX, mem_iova: %pFX\n", gpio_vpa, mem_iova); + + // gpio_hook is called by preserve_tegrachip() in gpio_tegra186.c -- don't call it here + // gpio_hook() diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/default.nix index 938fc3fab..3f1811644 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/default.nix @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 (_final: prev: { qemu = prev.qemu.overrideAttrs (_final: prev: { - patches = + patches = prev.patches ++ [ ./patches/0001-qemu-v8.1.3_bpmp-virt.patch diff --git a/modules/microvm/virtualization/microvm/qemu-gpio-guestvm.dtb b/modules/microvm/virtualization/microvm/qemu-gpio-guestvm.dtb index 9ae873329d31ccde68f9f7eae35821bf5da256a3..cf556aa6aaef33e1a2ad820ad195f645854c9c8a 100644 GIT binary patch delta 570 zcmez5dfrvz0`I@K3=B%=85kHW7#I|G0BH>%76f7eAO->^aiDm^Mvcvatb9Q3_Q?*S z3Y$fQikU1A0=djU%mTz0ftU%(2T}}op=^-cCm@E&DKIcFGEY7rQqKr7sF^G?WWZ+p z5mlc2TtsrRjhMA2KLbMp$R?0$Kmh70Mo}QU1j-iz>Qb1zK+IcGEwLaez92a#8z=(; zFx5trS;Wd4hsABhTbL3a2+uQcPe30IQQ?bpQYW delta 581 zcmX@_`pH$}0`I@K3=B%27#J8V7#I{T0BH>%76f7eAO->^C7}3>jT)N;B{_iH?I1Bu z9uUdE48(jud}#9n!O2XPXP`nXK>7|4GeP-4is1#64U+o{#4tGp1_nmH$!kRF84pc< zAgD0eM>Lr%b7a8gybx8MTrMawSz646QEGC6n7AO&e1;Mr202v-s78BohnTmdVPZj2 zd_i(fHc%7iQV`Gu@C Date: Tue, 14 May 2024 09:31:55 +0000 Subject: [PATCH 17/26] Updated testing kernel with readl and writel passthrough Guest proxy driver can passtrough low level readl and writel to host proxy. This might (meaning: still being tested) be necessary to allow guest to set up GPIO pins declared in guest's Device Tree. --- .../development/authorized_ssh_keys.nix | 1 + modules/common/development/ssh.nix | 5 +- .../patches/0003-gpio-virt-kernel.patch | 1895 +++++++++++++---- .../patches/0004-gpio-virt-drivers.patch | 88 +- .../microvm/qemu-gpio-guestvm.dtb | Bin 8911 -> 8962 bytes 5 files changed, 1500 insertions(+), 489 deletions(-) diff --git a/modules/common/development/authorized_ssh_keys.nix b/modules/common/development/authorized_ssh_keys.nix index 6899010a7..0056d4c10 100644 --- a/modules/common/development/authorized_ssh_keys.nix +++ b/modules/common/development/authorized_ssh_keys.nix @@ -15,6 +15,7 @@ "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEJ9ewKwo5FLj6zE30KnTn8+nw7aKdei9SeTwaAeRdJDAAAABHNzaDo= brian@minerva" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDdNDuKwAsAff4iFRfujo77W4cyAbfQHjHP57h/7tJde ville.ilvonen@unikie.com" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICKm9NtS/ZmrxQhY/pbRlX+9O1VaBEd8D9vojDtvS0Ru juliuskoskela@vega" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILnTMRhhsaZKKL1fwyXE6kRJkiTJwJxI4WoTAkUM99nV kisandst@kim-nvidia" # For ghaf-installer automated testing: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAolaKCuIUBQSBFGFZI1taNX+JTAr8edqUts7A6k2Kv7" diff --git a/modules/common/development/ssh.nix b/modules/common/development/ssh.nix index 3e38d68bb..3bb921f00 100644 --- a/modules/common/development/ssh.nix +++ b/modules/common/development/ssh.nix @@ -14,7 +14,10 @@ in }; config = mkIf cfg.enable { - services.openssh.enable = true; + services.openssh = { + enable = true; + settings.X11Forwarding = true; + }; users.users.root.openssh.authorizedKeys.keys = authorizedKeys; users.users.${config.ghaf.users.accounts.user}.openssh.authorizedKeys.keys = authorizedKeys; }; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch index 9c3e84c51..39e1aae3f 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch @@ -1,3 +1,37 @@ +diff --git a/drivers/gpio/gpio-irq-proxy.h b/drivers/gpio/gpio-irq-proxy.h +new file mode 100644 +index 000000000000..86ae48c95363 +--- /dev/null ++++ b/drivers/gpio/gpio-irq-proxy.h +@@ -0,0 +1,28 @@ ++#ifndef GPIO_IRQ_PROXY_H ++#define GPIO_IRQ_PROXY_H ++ ++/* passthrough hooks for low level functions sudh as readl adnn writel ++ * functions are mainly intended for GPIO passthrough ++ */ ++ ++ extern bool kernel_is_on_guest; ++ extern inline u32 readl_redirect( void * addr); ++ extern inline void writel_redirect( u32 value, void * addr); ++ ++ static inline u32 readl_x( void * addr) { ++ u32 ret; ++ if(kernel_is_on_guest) ++ ret = readl_redirect(addr); ++ else ++ ret = readl(addr); ++ return ret; ++ }; ++ ++ static inline void writel_x( u32 value, void * addr) { ++ if(kernel_is_on_guest) ++ writel_redirect(value, addr); ++ else ++ writel(value, addr); ++ }; ++ ++#endif diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index f66fc17faee4..629bc13aa4c3 100644 --- a/drivers/gpio/gpio-tegra.c @@ -247,7 +281,7 @@ index f66fc17faee4..629bc13aa4c3 100644 } subsys_initcall(tegra_gpio_init); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c -index 5e57824b283e..1db1dc2d2273 100644 +index 5e57824b283e..99bda33cadd4 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -44,7 +44,7 @@ @@ -259,7 +293,7 @@ index 5e57824b283e..1db1dc2d2273 100644 #define TEGRA186_GPIO_ENABLE_CONFIG_ENABLE BIT(0) #define TEGRA186_GPIO_ENABLE_CONFIG_OUT BIT(1) #define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_NONE (0x0 << 2) -@@ -179,6 +179,51 @@ +@@ -179,6 +179,55 @@ /**************************************************************/ @@ -271,23 +305,13 @@ index 5e57824b283e..1db1dc2d2273 100644 + + #include "gpiolib.h" + #include ++ #include "gpio-irq-proxy.h" // low level hooks for readl_x and writel_x + + #define GPIO_DEBUG -+ // #define GPIO_DEBUG_VERBOSE -+ -+ #ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #else -+ #define deb_info(fmt, ...) -+ #define deb_debug(fmt, ...) -+ #endif ++ #define GPIO_DEBUG_VERBOSE + -+ #ifdef GPIO_DEBUG_VERBOSE -+ #define deb_verbose deb_debug -+ #else -+ #define deb_verbose(fmt, ...) -+ #endif ++ bool kernel_is_on_guest = false; ++ EXPORT_SYMBOL_GPL(kernel_is_on_guest); + + int gpio_outloud = 0; + EXPORT_SYMBOL_GPL(gpio_outloud); @@ -299,6 +323,20 @@ index 5e57824b283e..1db1dc2d2273 100644 + +#endif + ++#ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++#else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ +/* this portion of code comes from copydrivers branch +// structures to synchronise get_tegra186_gpio_driver() +// it allows proxy drivers to copy preset gpio and driver to themselves @@ -311,7 +349,7 @@ index 5e57824b283e..1db1dc2d2273 100644 struct tegra_gpio_port { const char *name; unsigned int bank; -@@ -192,25 +237,25 @@ struct tegra186_pin_range { +@@ -192,25 +241,25 @@ struct tegra186_pin_range { }; struct tegra_gpio_soc { @@ -353,7 +391,7 @@ index 5e57824b283e..1db1dc2d2273 100644 }; struct tegra_gpio { -@@ -278,12 +323,16 @@ static struct tegra_gte_info tegra194_gte_info[] = { +@@ -278,12 +327,16 @@ static struct tegra_gte_info tegra194_gte_info[] = { static inline u32 tegra_gte_readl(struct tegra_gpio *tgi, u32 reg) { @@ -370,7 +408,7 @@ index 5e57824b283e..1db1dc2d2273 100644 __raw_writel(val, tgi->gte_regs + reg); } -@@ -307,6 +356,8 @@ u64 tegra_gte_read_fifo(struct tegra_gpio *tgi, u32 offset) +@@ -307,6 +360,8 @@ u64 tegra_gte_read_fifo(struct tegra_gpio *tgi, u32 offset) u32 aon_bits; u32 bit_index = 0; @@ -379,7 +417,7 @@ index 5e57824b283e..1db1dc2d2273 100644 /* Check if FIFO is empty */ while ((tegra_gte_readl(tgi, GTE_GPIO_TESTATUS) >> GTE_GPIO_TESTATUS_OCCUPANCY_SHIFT) & -@@ -348,6 +399,8 @@ int tegra_gte_enable_ts(struct tegra_gpio *tgi, u32 offset) +@@ -348,6 +403,8 @@ int tegra_gte_enable_ts(struct tegra_gpio *tgi, u32 offset) u32 val, mask, reg; int i = 0; @@ -388,7 +426,7 @@ index 5e57824b283e..1db1dc2d2273 100644 if (tgi->gte_enable == 1) { dev_err(tgi->gpio.parent, "timestamp is already enabled for gpio\n"); return -EINVAL; -@@ -381,6 +434,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) +@@ -381,6 +438,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) { u32 val, mask; @@ -397,7 +435,7 @@ index 5e57824b283e..1db1dc2d2273 100644 if (tgi->gte_enable == 0) { dev_err(tgi->gpio.parent, "timestamp is already disabled\n"); return 0; -@@ -405,6 +460,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) +@@ -405,6 +464,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) int tegra_gte_setup(struct tegra_gpio *tgi) { @@ -406,7 +444,7 @@ index 5e57824b283e..1db1dc2d2273 100644 tegra_gte_writel(tgi, GTE_GPIO_TECTRL, 0); tgi->gte_enable = 0; -@@ -418,6 +475,8 @@ tegra186_gpio_get_port(struct tegra_gpio *gpio, unsigned int *pin) +@@ -418,6 +479,8 @@ tegra186_gpio_get_port(struct tegra_gpio *gpio, unsigned int *pin) { unsigned int start = 0, i; @@ -415,7 +453,7 @@ index 5e57824b283e..1db1dc2d2273 100644 for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; -@@ -438,6 +497,8 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, +@@ -438,6 +501,8 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, const struct tegra_gpio_port *port; unsigned int offset; @@ -424,7 +462,7 @@ index 5e57824b283e..1db1dc2d2273 100644 port = tegra186_gpio_get_port(gpio, &pin); if (!port) return NULL; -@@ -453,6 +514,8 @@ static void __iomem *tegra186_gpio_get_secure(struct tegra_gpio *gpio, +@@ -453,6 +518,8 @@ static void __iomem *tegra186_gpio_get_secure(struct tegra_gpio *gpio, const struct tegra_gpio_port *port; unsigned int offset; @@ -433,7 +471,7 @@ index 5e57824b283e..1db1dc2d2273 100644 port = tegra186_gpio_get_port(gpio, &pin); if (!port) return NULL; -@@ -466,14 +529,17 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) +@@ -466,14 +533,17 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) void __iomem *secure; u32 val; @@ -455,7 +493,7 @@ index 5e57824b283e..1db1dc2d2273 100644 if ((val & (GPIO_SCR_SEC_ENABLE)) == 0) return true; -@@ -484,13 +550,15 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) +@@ -484,13 +554,15 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) return false; } @@ -472,7 +510,15 @@ index 5e57824b283e..1db1dc2d2273 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -505,7 +573,7 @@ static int tegra186_gpio_get_direction(struct gpio_chip *chip, +@@ -498,14 +570,14 @@ static int tegra186_gpio_get_direction(struct gpio_chip *chip, + if (WARN_ON(base == NULL)) + return -ENODEV; + +- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); ++ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); + if (value & TEGRA186_GPIO_ENABLE_CONFIG_OUT) + return GPIO_LINE_DIRECTION_OUT; + return GPIO_LINE_DIRECTION_IN; } @@ -481,7 +527,7 @@ index 5e57824b283e..1db1dc2d2273 100644 unsigned int offset) { struct tegra_gpio *gpio = gpiochip_get_data(chip); -@@ -513,6 +581,8 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, +@@ -513,6 +585,8 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, u32 value; int ret = 0; @@ -490,7 +536,26 @@ index 5e57824b283e..1db1dc2d2273 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -536,7 +606,7 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, +@@ -520,14 +594,14 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, + if (WARN_ON(base == NULL)) + return -ENODEV; + +- value = readl(base + TEGRA186_GPIO_OUTPUT_CONTROL); ++ value = readl_x(base + TEGRA186_GPIO_OUTPUT_CONTROL); + value |= TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED; +- writel(value, base + TEGRA186_GPIO_OUTPUT_CONTROL); ++ writel_x(value, base + TEGRA186_GPIO_OUTPUT_CONTROL); + +- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); ++ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); + value |= TEGRA186_GPIO_ENABLE_CONFIG_ENABLE; + value &= ~TEGRA186_GPIO_ENABLE_CONFIG_OUT; +- writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); ++ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); + + ret = pinctrl_gpio_direction_input(chip->base + offset); + if (ret < 0) +@@ -536,7 +610,7 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, return ret; } @@ -499,7 +564,7 @@ index 5e57824b283e..1db1dc2d2273 100644 unsigned int offset, int level) { struct tegra_gpio *gpio = gpiochip_get_data(chip); -@@ -544,6 +614,8 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, +@@ -544,6 +618,8 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, u32 value; int ret = 0; @@ -508,7 +573,26 @@ index 5e57824b283e..1db1dc2d2273 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -578,6 +650,8 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, +@@ -555,14 +631,14 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, + return -EINVAL; + + /* set the direction */ +- value = readl(base + TEGRA186_GPIO_OUTPUT_CONTROL); ++ value = readl_x(base + TEGRA186_GPIO_OUTPUT_CONTROL); + value &= ~TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED; +- writel(value, base + TEGRA186_GPIO_OUTPUT_CONTROL); ++ writel_x(value, base + TEGRA186_GPIO_OUTPUT_CONTROL); + +- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); ++ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); + value |= TEGRA186_GPIO_ENABLE_CONFIG_ENABLE; + value |= TEGRA186_GPIO_ENABLE_CONFIG_OUT; +- writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); ++ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); + ret = pinctrl_gpio_direction_output(chip->base + offset); + + if (ret < 0) +@@ -578,6 +654,8 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, struct tegra_gpio_saved_register *regs; void __iomem *base; @@ -517,7 +601,20 @@ index 5e57824b283e..1db1dc2d2273 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -606,6 +680,8 @@ static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, +@@ -586,9 +664,9 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, + return -EINVAL; + + regs = &gpio->gpio_rval[offset]; +- regs->conf = readl(base + TEGRA186_GPIO_ENABLE_CONFIG), +- regs->out = readl(base + TEGRA186_GPIO_OUTPUT_CONTROL), +- regs->val = readl(base + TEGRA186_GPIO_OUTPUT_VALUE), ++ regs->conf = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG), ++ regs->out = readl_x(base + TEGRA186_GPIO_OUTPUT_CONTROL), ++ regs->val = readl_x(base + TEGRA186_GPIO_OUTPUT_VALUE), + regs->restore_needed = true; + + if (dflags & GPIOD_FLAGS_BIT_DIR_OUT) +@@ -606,14 +684,16 @@ static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, int value; int ret; @@ -526,7 +623,17 @@ index 5e57824b283e..1db1dc2d2273 100644 base = tegra186_gpio_get_base(gpio, offset); if (WARN_ON(base == NULL)) return -EINVAL; -@@ -630,6 +706,8 @@ static int tegra_gpio_timestamp_read(struct gpio_chip *chip, unsigned offset, + + if (gpio->use_timestamp) { +- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); ++ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); + value |= TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMP_FUNC; +- writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); ++ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); + if (enable) + ret = tegra_gte_enable_ts(gpio, offset); + else +@@ -630,6 +710,8 @@ static int tegra_gpio_timestamp_read(struct gpio_chip *chip, unsigned offset, struct tegra_gpio *tgi = gpiochip_get_data(chip); int ret; @@ -535,7 +642,7 @@ index 5e57824b283e..1db1dc2d2273 100644 if (tgi->use_timestamp) { *ts = tegra_gte_read_fifo(tgi, offset); ret = 0; -@@ -645,6 +723,8 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) +@@ -645,40 +727,58 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) void __iomem *base; u32 value; @@ -544,7 +651,16 @@ index 5e57824b283e..1db1dc2d2273 100644 base = tegra186_gpio_get_base(gpio, offset); if (WARN_ON(base == NULL)) return -ENODEV; -@@ -658,17 +738,21 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) + +- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); ++ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); + if (value & TEGRA186_GPIO_ENABLE_CONFIG_OUT) +- value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); ++ value = readl_x(base + TEGRA186_GPIO_OUTPUT_VALUE); + else +- value = readl(base + TEGRA186_GPIO_INPUT); ++ value = readl_x(base + TEGRA186_GPIO_INPUT); + return value & BIT(0); } @@ -569,12 +685,17 @@ index 5e57824b283e..1db1dc2d2273 100644 if (WARN_ON(base == NULL)) return; -@@ -679,6 +763,18 @@ static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, +- value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); ++ value = readl_x(base + TEGRA186_GPIO_OUTPUT_VALUE); + if (level == 0) + value &= ~TEGRA186_GPIO_OUTPUT_VALUE_HIGH; + else value |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH; - writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); +- writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); ++ writel_x(value, base + TEGRA186_GPIO_OUTPUT_VALUE); + -+ deb_verbose("(2): exiting -- value is %d, base is %p\n", value, (void *)base); ++ deb_verbose("(2): exiting -- value is %d, base is nn%p\n", value, (void *)base); +} + +void tegra186_gpio_set_by_name(const char *name, unsigned int offset, @@ -588,7 +709,7 @@ index 5e57824b283e..1db1dc2d2273 100644 } static int tegra186_gpio_set_config(struct gpio_chip *chip, -@@ -689,6 +785,8 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, +@@ -689,6 +789,8 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, u32 debounce, value; void __iomem *base; @@ -597,7 +718,22 @@ index 5e57824b283e..1db1dc2d2273 100644 base = tegra186_gpio_get_base(gpio, offset); if (base == NULL) return -ENXIO; -@@ -725,6 +823,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) +@@ -708,11 +810,11 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, + debounce = DIV_ROUND_UP(debounce, USEC_PER_MSEC); + + value = TEGRA186_GPIO_DEBOUNCE_CONTROL_THRESHOLD(debounce); +- writel(value, base + TEGRA186_GPIO_DEBOUNCE_CONTROL); ++ writel_x(value, base + TEGRA186_GPIO_DEBOUNCE_CONTROL); + +- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); ++ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); + value |= TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE; +- writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); ++ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); + + return 0; + } +@@ -725,6 +827,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) unsigned int i, j; int err; @@ -606,7 +742,7 @@ index 5e57824b283e..1db1dc2d2273 100644 if (!gpio->soc->pinmux || gpio->soc->num_pin_ranges == 0) return 0; -@@ -768,6 +868,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, +@@ -768,6 +872,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, struct tegra_gpio *gpio = gpiochip_get_data(chip); unsigned int port, pin, i, offset = 0; @@ -615,7 +751,67 @@ index 5e57824b283e..1db1dc2d2273 100644 if (WARN_ON(chip->of_gpio_n_cells < 2)) return -EINVAL; -@@ -1037,6 +1139,8 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -803,7 +909,7 @@ static void tegra186_irq_ack(struct irq_data *data) + if (WARN_ON(base == NULL)) + return; + +- writel(1, base + TEGRA186_GPIO_INTERRUPT_CLEAR); ++ writel_x(1, base + TEGRA186_GPIO_INTERRUPT_CLEAR); + } + + static void tegra186_irq_mask(struct irq_data *data) +@@ -817,9 +923,9 @@ static void tegra186_irq_mask(struct irq_data *data) + if (WARN_ON(base == NULL)) + return; + +- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); ++ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); + value &= ~TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT; +- writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); ++ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); + } + + static void tegra186_irq_unmask(struct irq_data *data) +@@ -833,9 +939,9 @@ static void tegra186_irq_unmask(struct irq_data *data) + if (WARN_ON(base == NULL)) + return; + +- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); ++ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); + value |= TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT; +- writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); ++ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); + } + + static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) +@@ -849,7 +955,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) + if (WARN_ON(base == NULL)) + return -ENODEV; + +- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); ++ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); + value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK; + value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL; + +@@ -883,7 +989,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) + return -EINVAL; + } + +- writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); ++ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); + + if ((type & IRQ_TYPE_EDGE_BOTH) == 0) + irq_set_handler_locked(data, handle_level_irq); +@@ -930,7 +1036,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) + if (j == gpio->num_irqs_per_bank) + goto skip; + +- value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1)); ++ value = readl_x(base + TEGRA186_GPIO_INTERRUPT_STATUS(1)); + + for_each_set_bit(pin, &value, port->pins) { + irq = irq_find_mapping(domain, offset + pin); +@@ -1037,6 +1143,8 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) unsigned int i, j; u32 value; @@ -624,7 +820,37 @@ index 5e57824b283e..1db1dc2d2273 100644 for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; unsigned int offset, p = port->port; -@@ -1107,6 +1211,190 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +@@ -1044,7 +1152,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) + + base = gpio->secure + port->bank * 0x1000 + 0x800; + +- value = readl(base + TEGRA186_GPIO_CTL_SCR); ++ value = readl_x(base + TEGRA186_GPIO_CTL_SCR); + + /* + * For controllers that haven't been locked down yet, make +@@ -1058,7 +1166,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) + */ + for (j = 0; j < gpio->num_irqs_per_bank; j++) { + dev_dbg(dev, "programming default interrupt routing for port %s\n", +- port->name); ++ port->name); + + offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j); + +@@ -1073,9 +1181,9 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) + */ + + if (j == 0) { +- value = readl(base + offset); ++ value = readl_x(base + offset); + value = BIT(port->pins) - 1; +- writel(value, base + offset); ++ writel_x(value, base + offset); + } + } + } +@@ -1107,6 +1215,182 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) return -EINVAL; } @@ -672,7 +898,7 @@ index 5e57824b283e..1db1dc2d2273 100644 + extern int tegra186_gpio_add_pin_ranges_redirect(struct gpio_chip *chip); + + static inline void gpio_hook(struct tegra_gpio *gpio) { -+ deb_debug("Entering function with pointer: %p and gpio: %s", gpio, gpio->gpio.label); ++ deb_debug("Hooking functions for %s", gpio->gpio.label); + gpio->gpio.request = gpiochip_generic_request_redirect; + gpio->gpio.free = gpiochip_generic_free_redirect; + gpio->gpio.get_direction = tegra186_gpio_get_direction_redirect; @@ -686,14 +912,12 @@ index 5e57824b283e..1db1dc2d2273 100644 + gpio->gpio.suspend_configure = tegra_gpio_suspend_configure_redirect; + gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges_redirect; + gpio->gpio.base = -1; -+ -+ deb_debug("gpio functions are hooked\n"); + } +#endif + +// this function sets the standard bindings used by the host driver +static inline void gpio_unhook(struct tegra_gpio *gpio) { -+ deb_debug("Entering function with pointer: %p and gpio: %s", gpio, gpio->gpio.label); ++ deb_debug("Setting default functions for %s", gpio->gpio.label); + gpio->gpio.request = gpiochip_generic_request; + gpio->gpio.free = gpiochip_generic_free; + gpio->gpio.get_direction = tegra186_gpio_get_direction; @@ -707,8 +931,6 @@ index 5e57824b283e..1db1dc2d2273 100644 + gpio->gpio.suspend_configure = tegra_gpio_suspend_configure; + gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; + gpio->gpio.base = -1; -+ -+ deb_debug("gpio functions are unhooked\n"); +} + +#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) @@ -717,7 +939,6 @@ index 5e57824b283e..1db1dc2d2273 100644 + + #define MAX_CHIP 2 // check this value against value in gpio_host-proxy.h + -+ static int gpio_chip_count = 0; + struct tegra_gpio *tegra_gpio_hosts[MAX_CHIP] = {NULL, NULL}; + atomic_t tegra_gpio_hosts_ready = ATOMIC_INIT(0); + @@ -751,22 +972,15 @@ index 5e57824b283e..1db1dc2d2273 100644 + * the preserve functionsalso assist in allocation and deallocaton, setting up and unseting + * of proxy related data */ + static void preserve_tegrachip(struct tegra_gpio *tegrachip) { -+ struct gpio_chip *gpiochip = &tegrachip->gpio; -+ int id = gpiochip->gpiodev->id; ++ struct gpio_chip *gpiochip = &(tegrachip->gpio); ++ static int gpio_chip_count = 0; + -+ deb_debug("id = %d\n", id); -+ -+ if( id != gpio_chip_count) { -+ // we assume gpiochip0 will be registered in slot 0 and gpiochip1 in slot 1 -+ // if this nonfatal error triggers, we register using 'id' as an index ansyhow -+ deb_debug("gpio device id mismatch, gpio_chip_count = %d, id = %d\n", gpio_chip_count, id); -+ } + if (gpio_chip_count >= MAX_CHIP) { + pr_err("GPIO, *ERROR* maximum chip count is exceeded (%d)", gpio_chip_count); + } + else { -+ tegra_gpio_hosts[id] = tegrachip; -+ deb_debug("put chip %s in list for passthrough in slot %d [0..1]\n", gpiochip->label, id); ++ tegra_gpio_hosts[gpio_chip_count] = tegrachip; ++ deb_debug("put chip %s in list for passthrough in slot %d [0..1]\n", gpiochip->label, gpio_chip_count); + }; + atomic_set(&tegra_gpio_hosts_ready, ++gpio_chip_count); + } @@ -777,12 +991,12 @@ index 5e57824b283e..1db1dc2d2273 100644 + * (paranoia because drivers are built in) */ + void unpreserve_all_tegrachips(void) { + struct tegra_gpio ** tegrachip; -+ int i = 0; ++ int i, timeout = 0; + + // wait until tegra driver has set the tegra_gpio_hosts array -- this seems a bit paranoid + while (atomic_read(&tegra_gpio_hosts_ready) != MAX_CHIP) { + msleep(100); // Sleep briefly instead of looping infinitely. -+ if( i++ > 120 ) { ++ if( timeout++ > 120 ) { + pr_err("GPIO could not access tegra_gpio chip array\n"); + return; + } @@ -797,7 +1011,9 @@ index 5e57824b283e..1db1dc2d2273 100644 + EXPORT_SYMBOL_GPL(unpreserve_all_tegrachips); + + /* find_chip_by_id -+ * replacement for find_chip_by_name, because it is slightly faster */ ++ * replacement for find_chip_by_name, because it is slightly faster ++ * one doubtful assumption is that chip pointers are numbered by the driver ++ * in the same order preserve_tegrachip recirds them */ + inline struct gpio_chip * find_chip_by_id(int id) { + int i = 0; + while (atomic_read(&tegra_gpio_hosts_ready) != MAX_CHIP) { @@ -811,18 +1027,19 @@ index 5e57824b283e..1db1dc2d2273 100644 + } + EXPORT_SYMBOL_GPL(find_chip_by_id); +#endif ++ ++extern int devm_gpiochip_add_data__redirect(struct device *dev, struct gpio_chip *gc, void *data); + static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; -@@ -1120,17 +1408,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1120,17 +1404,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) int value; void __iomem *base; -+ bool kernel_is_on_guest = false; + static bool guest_proxy_is_set_up = false; + -+ deb_debug("Probing gpio\n"); ++ deb_debug("Probing gpio"); + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); - if (!gpio) @@ -832,47 +1049,66 @@ index 5e57824b283e..1db1dc2d2273 100644 + } + + #if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) -+ + + deb_debug("GPIO Proxy code\n"); + -+ // If virtual-pa node is defined, it means that we are using a virtual GPIO ++ // If virtual-pa node is defined, it means that we are using a GPIO proxy + err = of_property_read_u64(pdev->dev.of_node, "virtual-pa", &gpio_vpa); + if(!err) { ++ // error in reading virtual-pa is non fatal, it only means we are on host. + deb_info("GPIO kernel is on guest, virtual-pa: 0x%llx\n", gpio_vpa); + kernel_is_on_guest = true; + } + #endif - ++ gpio->soc = of_device_get_match_data(&pdev->dev); gpio->gpio.label = gpio->soc->name; gpio->gpio.parent = &pdev->dev; - gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security"); +- gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security"); - if (IS_ERR(gpio->secure)) -+ if (IS_ERR(gpio->secure)) { -+ pr_err("GPIO *ERROR* devm_platform_ioremap_resource_byname pdev->name=\"%s\"", pdev->name); - return PTR_ERR(gpio->secure); -+ } +- return PTR_ERR(gpio->secure); ++ gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security"); ++ if (IS_ERR(gpio->secure)) { ++ pr_err("GPIO *ERROR* devm_platform_ioremap_resource_byname pdev->name=\"%s\"", pdev->name); ++ return PTR_ERR(gpio->secure); ++ } + -+ deb_debug("num of ports = %d\n", gpio->soc->num_ports); ++ deb_verbose("num of ports = %d", gpio->soc->num_ports); /* count the number of banks in the controller */ for (i = 0; i < gpio->soc->num_ports; i++) -@@ -1139,6 +1450,13 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1139,11 +1446,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->num_banks++; -+ #if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) -+ // in we are on guest VM -+ // if(kernel_is_on_guest) -+ if(false) -+ goto quick_end_without_irq_setup; -+ #endif ++ deb_verbose("num of banks = %d", gpio->num_banks); + gpio->base = devm_platform_ioremap_resource_byname(pdev, "gpio"); if (IS_ERR(gpio->base)) return PTR_ERR(gpio->base); -@@ -1172,14 +1490,18 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + +- gpio->gpio_rval = devm_kzalloc(&pdev->dev, gpio->soc->num_ports * 8 * ++ deb_verbose("gpio base = 0x%llx", (long long unsigned int)gpio->base); ++ ++ gpio->gpio_rval = devm_kzalloc(&pdev->dev, gpio->soc->num_ports * 8 * + sizeof(*gpio->gpio_rval), GFP_KERNEL); + if (!gpio->gpio_rval) + return -ENOMEM; +@@ -1154,9 +1465,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + return -EINVAL; + } + ++ deb_verbose("node pointer = 0x%llx", (long long unsigned int)np); ++ + gpio->use_timestamp = of_property_read_bool(np, "use-timestamp"); + + if (gpio->use_timestamp) { ++ deb_verbose("use_timestamp"); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gte"); + if (!res) { + dev_err(&pdev->dev, "Missing gte MEM resource\n"); +@@ -1172,14 +1486,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) } err = platform_irq_count(pdev); @@ -884,8 +1120,11 @@ index 5e57824b283e..1db1dc2d2273 100644 gpio->num_irq = err; - err = tegra186_gpio_irqs_per_bank(gpio); +- err = tegra186_gpio_irqs_per_bank(gpio); - if (err < 0) ++ deb_verbose("num_irq = %d", gpio->num_irq); ++ ++ err = tegra186_gpio_irqs_per_bank(gpio); + if (err < 0) { + pr_err("GPIO tegra186_gpio_irqs_per_bank\n"); return err; @@ -893,7 +1132,7 @@ index 5e57824b283e..1db1dc2d2273 100644 gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), GFP_KERNEL); -@@ -1188,27 +1510,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1188,27 +1508,44 @@ static int tegra186_gpio_probe(struct platform_device *pdev) for (i = 0; i < gpio->num_irq; i++) { err = platform_get_irq(pdev, i); @@ -921,57 +1160,21 @@ index 5e57824b283e..1db1dc2d2273 100644 - gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; - - gpio->gpio.base = -1; -+ // gpio function pionters were set here in stock code, now moved to the end of this function - - for (i = 0; i < gpio->soc->num_ports; i++) - gpio->gpio.ngpio += gpio->soc->ports[i].pins; -@@ -1229,6 +1539,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) - return -ENOMEM; - - names[offset + j] = name; -+ // deb_verbose("GPIO, name=\n", name); - } - - offset += port->pins; -@@ -1315,9 +1626,11 @@ static int tegra186_gpio_probe(struct platform_device *pdev) - - platform_set_drvdata(pdev, gpio); - -- err = devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio); -- if (err < 0) -- return err; -+ err = devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio); -+ if (err < 0) { -+ pr_err("GPIO devm_gpiochip_add_data\n"); -+ return err; -+ } - - if (gpio->soc->is_hw_ts_sup) { - for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) { -@@ -1343,10 +1656,44 @@ static int tegra186_gpio_probe(struct platform_device *pdev) - if (gpio->use_timestamp) - tegra_gte_setup(gpio); - -- return 0; + #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) -+ quick_end_without_irq_setup: + -+ deb_debug("GPIO Proxy code\n"); + ++ if (gpio->use_timestamp) ++ tegra_gte_setup(gpio); ++ ++ deb_debug("GPIO Guest init section\n"); + if( kernel_is_on_guest ) { + if( ! guest_proxy_is_set_up ) { -+ deb_debug("A"); + ret = tegra_gpio_guest_init(); + guest_proxy_is_set_up = true; -+ deb_debug("B"); + } -+ // hook for all function pointers in default driver in guest VM" + // guest should use gpio_hook() for its "host" driver -+ deb_debug("C"); + gpio_hook(gpio); -+ deb_debug("D"); + } -+ // error in reading virtual-pa is non fatal, it only means we are on host. + else { + // gpio_unhook is the same as standard settings + // unhooked pointers are for the host driver on host only @@ -986,6 +1189,94 @@ index 5e57824b283e..1db1dc2d2273 100644 + deb_debug("Setting standard gpio functions for a non-proxy compile of driver\n") + BUG_ON(gpio_vpa != 0); // assert we do not set up the vpa driver, because non-proxy + gpio_unhook(gpio); ++ #endif + + for (i = 0; i < gpio->soc->num_ports; i++) + gpio->gpio.ngpio += gpio->soc->ports[i].pins; +@@ -1229,6 +1566,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + return -ENOMEM; + + names[offset + j] = name; ++ // deb_verbose("GPIO, name=\n", name); + } + + offset += port->pins; +@@ -1260,6 +1598,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + irq->parent_handler_data = gpio; + irq->num_parents = gpio->num_irq; + ++//DEBUG ++deb_verbose("gpio->gpio.of_node = 0x%llx, pdev->dev.of_node = 0x%llx", (long long unsigned int)gpio->gpio.of_node, (long long unsigned int)pdev->dev.of_node); + + /* + * To simplify things, use a single interrupt per bank for now. Some +@@ -1283,8 +1623,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + irq->parents = gpio->irq; + } + ++ #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) ++ // we cannot physically set up irq on guest ++ if(kernel_is_on_guest) { ++ #endif + if (gpio->soc->num_irqs_per_bank > 1) + tegra186_gpio_init_route_mapping(gpio); ++ #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) ++ } ++ #endif + + np = of_find_matching_node(NULL, tegra186_pmc_of_match); + if (!of_device_is_available(np)) +@@ -1315,9 +1662,24 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + + platform_set_drvdata(pdev, gpio); + +- err = devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio); +- if (err < 0) +- return err; ++ #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) ++ if(kernel_is_on_guest) { ++ err = devm_gpiochip_add_data__redirect(&pdev->dev, &gpio->gpio, gpio); ++ } ++ else ++ err = devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio); ++ #else ++ err = devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio); ++ #endif ++ ++ if (err < 0) { ++ pr_err("GPIO *ERROR* devm_gpiochip_add_data\n"); ++ return err; ++ } ++ ++ #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) ++ if(kernel_is_on_guest) goto guest_skip; ++ #endif + + if (gpio->soc->is_hw_ts_sup) { + for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) { +@@ -1329,24 +1691,24 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + if (WARN_ON(base == NULL)) + return -EINVAL; + +- value = readl(base + ++ value = readl_x(base + + TEGRA186_GPIO_ENABLE_CONFIG); + value |= + TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMP_FUNC; +- writel(value, ++ writel_x(value, + base + TEGRA186_GPIO_ENABLE_CONFIG); + } + offset += port->pins; + } + } + +- if (gpio->use_timestamp) +- tegra_gte_setup(gpio); +- +- return 0; ++ #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) ++ guest_skip: + #endif + return 0; } @@ -995,7 +1286,20 @@ index 5e57824b283e..1db1dc2d2273 100644 static int tegra_gpio_resume_early(struct device *dev) { struct tegra_gpio *gpio = dev_get_drvdata(dev); -@@ -1723,7 +2070,9 @@ static struct platform_driver tegra186_gpio_driver = { +@@ -1366,9 +1728,9 @@ static int tegra_gpio_resume_early(struct device *dev) + + regs->restore_needed = false; + +- writel(regs->val, base + TEGRA186_GPIO_OUTPUT_VALUE); +- writel(regs->out, base + TEGRA186_GPIO_OUTPUT_CONTROL); +- writel(regs->conf, base + TEGRA186_GPIO_ENABLE_CONFIG); ++ writel_x(regs->val, base + TEGRA186_GPIO_OUTPUT_VALUE); ++ writel_x(regs->out, base + TEGRA186_GPIO_OUTPUT_CONTROL); ++ writel_x(regs->conf, base + TEGRA186_GPIO_ENABLE_CONFIG); + } + + return 0; +@@ -1723,7 +2085,9 @@ static struct platform_driver tegra186_gpio_driver = { .probe = tegra186_gpio_probe, .remove = tegra186_gpio_remove, }; @@ -1205,6 +1509,56 @@ index 2613881a66e6..1b052acf3753 100644 + cdev_device_del(&gdev->chrdev, &gdev->dev); } +diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c +index 7dbce4c4ebdf..a8ec8a115c61 100644 +--- a/drivers/gpio/gpiolib-devres.c ++++ b/drivers/gpio/gpiolib-devres.c +@@ -503,6 +503,7 @@ static void devm_gpio_chip_release(struct device *dev, void *res) + * gc->base is invalid or already associated with a different chip. + * Otherwise it returns zero as a success code. + */ ++ + int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, void *data, + struct lock_class_key *lock_key, + struct lock_class_key *request_key) +@@ -527,3 +528,37 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, vo + return 0; + } + EXPORT_SYMBOL_GPL(devm_gpiochip_add_data_with_key); ++ ++/* redirect function added for gpio passthrough in Guest VM ++ * Virtual machine does not have psysical access to resources ++ */ ++ ++//from kernel-5.10/drivers/gpio/gpiolib-devres.c ++// a note for redirect ++// we redirect ++ ++extern int gpiochip_add_data__redirect(struct gpio_chip *gc, void *data); ++ ++int devm_gpiochip_add_data__redirect(struct device *dev, struct gpio_chip *gc, void *data) ++{ ++ struct gpio_chip **ptr; ++ int ret; ++ ++ ptr = devres_alloc(devm_gpio_chip_release, sizeof(*ptr), ++ GFP_KERNEL); ++ if (!ptr) ++ return -ENOMEM; ++ ++ ret = gpiochip_add_data__redirect (gc, data); ++ if (ret < 0) { ++ devres_free(ptr); ++ return ret; ++ } ++ ++ *ptr = gc; ++ devres_add(dev, ptr); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(devm_gpiochip_add_data__redirect); ++ diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index 30e2476a6dc4..aab40f1cd52f 100644 --- a/drivers/gpio/gpiolib-legacy.c @@ -1258,6 +1612,174 @@ index 30e2476a6dc4..aab40f1cd52f 100644 for (i = 0; i < num; i++, array++) { err = gpio_request_one(array->gpio, array->flags, array->label); if (err) +diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c +index 647e77db82b1..2134815919de 100644 +--- a/drivers/gpio/gpiolib-of.c ++++ b/drivers/gpio/gpiolib-of.c +@@ -24,6 +24,29 @@ + #include "gpiolib.h" + #include "gpiolib-of.h" + ++#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++ ++#include "gpio-irq-proxy.h" // low level hooks for readl_x and writel_x ++ ++#define GPIO_DEBUG ++#define GPIO_DEBUG_VERBOSE ++ ++#endif // CONFIG_TEGRA_GPIO_GUEST_PROXY and CONFIG_TEGRA_GPIO_HOST_PROXY ++ ++#ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++#else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ + /** + * of_gpio_spi_cs_get_count() - special GPIO counting for SPI + * @dev: Consuming device +@@ -1044,6 +1067,9 @@ static void of_gpiochip_init_valid_mask(struct gpio_chip *chip) + } + }; + ++// debug ++#include <../drivers/pinctrl/core.h> ++ + #ifdef CONFIG_PINCTRL + static int of_gpiochip_add_pin_range(struct gpio_chip *chip) + { +@@ -1066,12 +1092,34 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) + if (ret) + break; + +- pctldev = of_pinctrl_get(pinspec.np); ++// DEBUG ++if(pinspec.args_count == 0) deb_debug("no pins found in DT based on gpio-ranges"); ++deb_debug("node pointers, in=0x%llx, out=i0x%llx", (long long unsigned int)np, (long long unsigned int)pinspec.np); ++for(index=0; index < pinspec.args_count; index++) ++deb_debug("node index: %d, value: %d", index, pinspec.args[index]); ++ ++// FIXIT -- removed for debug ++// FIXIT ++// FIXIT ++// FIXIT -- we need this function to set pinranges ... a lot of pin data can be dummy ++// FIXIT ++// FIXIT ++// pctldev = kmalloc(sizeof(struct pinctrl_dev), GFP_KERNEL); // kmalloc is for debug use ++// memset(pctldev, 0, sizeof(struct pinctrl_dev)); // menset is a debug ++deb_verbose("np we search is: 0x%llx", (long long unsigned int)pinspec.np); // the BUG? np we search is: (____ptrval____) ++ pctldev = of_pinctrl_get(pinspec.np); + of_node_put(pinspec.np); ++//FIXIT - debug ++ + if (!pctldev) ++//FIXIT - debug ++{ deb_verbose("**A"); + return -EPROBE_DEFER; ++} ++deb_verbose("**a"); + + if (pinspec.args[2]) { ++deb_verbose("**b"); + if (group_names) { + of_property_read_string_index(np, + group_names_propname, +@@ -1082,6 +1130,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) + break; + } + } ++deb_verbose("**c"); + /* npins != 0: linear range */ + ret = gpiochip_add_pin_range(chip, + pinctrl_dev_get_devname(pctldev), +@@ -1089,7 +1138,11 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) + pinspec.args[1], + pinspec.args[2]); + if (ret) ++deb_verbose("**d"); ++//FIXIT - debug ++{ deb_verbose("**B"); + return ret; ++} + } else { + /* npins == 0: special range */ + if (pinspec.args[1]) { +@@ -1119,7 +1172,10 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) + ret = gpiochip_add_pingroup_range(chip, pctldev, + pinspec.args[0], name); + if (ret) ++//FIXIT - debug ++{ deb_verbose("C"); + return ret; ++} + } + } + +@@ -1133,7 +1189,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) { return 0; } + int of_gpiochip_add(struct gpio_chip *chip) + { + int ret; +- ++ + if (!chip->of_node) + return 0; + +@@ -1164,3 +1220,47 @@ void of_gpiochip_remove(struct gpio_chip *chip) + { + of_node_put(chip->of_node); + } ++ ++int of_gpiochip_add__redirect(struct gpio_chip *chip) ++{ ++ int ret; ++ ++deb_verbose("1.entry"); ++ if (!chip->of_node) ++ return 0; ++ ++deb_verbose("2"); ++ if (!chip->of_xlate) { ++ chip->of_gpio_n_cells = 2; ++ chip->of_xlate = of_gpio_simple_xlate; ++ } ++ ++deb_verbose("3"); ++ if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS) ++ return -EINVAL; ++ ++deb_verbose("4"); ++ of_gpiochip_init_valid_mask(chip); ++ ++deb_verbose("5"); ++ ret = of_gpiochip_add_pin_range(chip); ++ if (ret) ++ return ret; ++ ++deb_verbose("6"); ++ of_node_get(chip->of_node); ++ ++deb_verbose("7"); ++// ret = of_gpiochip_scan_gpios(chip); ++ if (ret) ++ of_node_put(chip->of_node); ++ ++deb_verbose("8.return"); ++ return ret; ++} ++ ++void of_gpiochip_remove_redirect(struct gpio_chip *chip) ++{ ++ of_node_put(chip->of_node); ++} ++ diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 3ef71ca242ba..bf62aeee66a1 100644 --- a/drivers/gpio/gpiolib-sysfs.c @@ -1289,27 +1811,40 @@ index 3ef71ca242ba..bf62aeee66a1 100644 /* If buf is not a number then try to find by name */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c -index 50abb1c20df0..9e466eeff297 100644 +index 50abb1c20df0..7152dc772a61 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c -@@ -31,6 +31,16 @@ +@@ -31,6 +31,29 @@ #define CREATE_TRACE_POINTS #include -+// #define GPIO_DEBUG ++ ++#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++#include "gpio-irq-proxy.h" // low level hooks for readl_x and writel_x ++ ++#define GPIO_DEBUG ++#define GPIO_DEBUG_VERBOSE ++ ++#endif // CONFIG_TEGRA_GPIO_GUEST_PROXY and CONFIG_TEGRA_GPIO_HOST_PROXY + +#ifdef GPIO_DEBUG -+#define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) -+#define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) +#else -+#define deb_info(fmt, ...) -+#define deb_debug(fmt, ...) ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) +#endif + /* Implementation infrastructure for GPIO interfaces. * * The GPIO programming interface allows for inlining speed-critical -@@ -45,11 +55,11 @@ +@@ -45,11 +68,11 @@ * * Otherwise, minimize overhead in what may be bitbanging codepaths. */ @@ -1323,110 +1858,153 @@ index 50abb1c20df0..9e466eeff297 100644 /* Device and char device-related information */ static DEFINE_IDA(gpio_ida); -@@ -105,6 +115,8 @@ struct gpio_desc *gpio_to_desc(unsigned gpio) +@@ -105,6 +128,8 @@ struct gpio_desc *gpio_to_desc(unsigned gpio) { struct gpio_device *gdev; unsigned long flags; + -+ deb_debug("\n"); ++ deb_verbose("\n"); spin_lock_irqsave(&gpio_lock, flags); -@@ -140,6 +152,8 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, +@@ -140,6 +165,8 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, { struct gpio_device *gdev = gc->gpiodev; -+ deb_debug("HW Number %u\n", hwnum); ++ deb_verbose("HW Number %u\n", hwnum); + if (hwnum >= gdev->ngpio) return ERR_PTR(-EINVAL); -@@ -170,6 +184,8 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); +@@ -170,6 +197,8 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); */ struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!desc || !desc->gdev) return NULL; return desc->gdev->chip; -@@ -214,6 +230,8 @@ int gpiod_get_direction(struct gpio_desc *desc) +@@ -183,6 +212,7 @@ static int gpiochip_find_base(int ngpio) + int base = ARCH_NR_GPIOS - ngpio; + + list_for_each_entry_reverse(gdev, &gpio_devices, list) { ++ deb_verbose("entry 0x%llx", (long long unsigned int)gdev); + /* found a free space? */ + if (gdev->base + gdev->ngpio <= base) + break; +@@ -214,6 +244,8 @@ int gpiod_get_direction(struct gpio_desc *desc) unsigned offset; int ret; -+ deb_debug("name=%s, label=%s\n", desc->name, desc->label); ++ deb_verbose("name=%s, label=%s\n", desc->name, desc->label); + gc = gpiod_to_chip(desc); offset = gpio_chip_hwgpio(desc); -@@ -253,6 +271,8 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) +@@ -253,9 +285,12 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) { struct gpio_device *prev, *next; -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (list_empty(&gpio_devices)) { /* initial entry in list */ list_add_tail(&gdev->list, &gpio_devices); -@@ -301,6 +321,8 @@ struct gpio_desc *gpio_name_to_desc(const char * const name) ++deb_info("debug 1"); + return 0; + } + +@@ -263,6 +298,7 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) + if (gdev->base + gdev->ngpio <= next->base) { + /* add before first entry */ + list_add(&gdev->list, &gpio_devices); ++deb_info("debug 2"); + return 0; + } + +@@ -270,18 +306,21 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) + if (prev->base + prev->ngpio <= gdev->base) { + /* add behind last entry */ + list_add_tail(&gdev->list, &gpio_devices); ++deb_info("debug 3"); + return 0; + } + + list_for_each_entry_safe(prev, next, &gpio_devices, list) { + /* at the end of the list */ + if (&next->list == &gpio_devices) ++{ deb_info("debug 4, prev= 0x%p, next = 0x%p", prev, next); + break; +- ++} + /* add between prev and next */ + if (prev->base + prev->ngpio <= gdev->base + && gdev->base + gdev->ngpio <= next->base) { + list_add(&gdev->list, &prev->list); ++deb_info("debug 5"); + return 0; + } + } +@@ -301,6 +340,8 @@ struct gpio_desc *gpio_name_to_desc(const char * const name) struct gpio_device *gdev; unsigned long flags; -+ // deb_debug("\n"); ++ // deb_verbose("\n"); + if (!name) return NULL; -@@ -340,6 +362,8 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) +@@ -340,6 +381,8 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) struct gpio_device *gdev = gc->gpiodev; int i; -+ deb_debug("\n"); ++ deb_verbose("\n"); + /* First check all names if they are unique */ for (i = 0; i != gc->ngpio; ++i) { struct gpio_desc *gpio; -@@ -375,6 +399,8 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip) +@@ -375,6 +418,8 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip) int ret, i; int count; -+ deb_debug("\n"); ++ deb_verbose("\n"); + count = fwnode_property_string_array_count(fwnode, "gpio-line-names"); if (count < 0) return 0; -@@ -409,6 +435,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) +@@ -409,6 +454,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) { unsigned long *p; -+ deb_debug("\n"); ++ deb_verbose("\n"); + p = bitmap_alloc(gc->ngpio, GFP_KERNEL); if (!p) return NULL; -@@ -421,6 +449,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) +@@ -421,6 +468,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask)) return 0; -@@ -433,6 +463,8 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) +@@ -433,6 +482,8 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) static int gpiochip_init_valid_mask(struct gpio_chip *gc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (gc->init_valid_mask) return gc->init_valid_mask(gc, gc->valid_mask, -@@ -443,12 +475,16 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) +@@ -443,12 +494,16 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_free_valid_mask(struct gpio_chip *gc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + bitmap_free(gc->valid_mask); gc->valid_mask = NULL; @@ -1434,30 +2012,30 @@ index 50abb1c20df0..9e466eeff297 100644 static int gpiochip_add_pin_ranges(struct gpio_chip *gc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (gc->add_pin_ranges) return gc->add_pin_ranges(gc); -@@ -458,6 +494,8 @@ static int gpiochip_add_pin_ranges(struct gpio_chip *gc) +@@ -458,6 +513,8 @@ static int gpiochip_add_pin_ranges(struct gpio_chip *gc) bool gpiochip_line_is_valid(const struct gpio_chip *gc, unsigned int offset) { -+ deb_debug("chip %s, offset=%u\n", gc->label, offset); ++ deb_verbose("chip %s, offset=%u\n", gc->label, offset); + /* No mask means all valid */ if (likely(!gc->valid_mask)) return true; -@@ -470,6 +508,8 @@ static void gpiodevice_release(struct device *dev) +@@ -470,6 +527,8 @@ static void gpiodevice_release(struct device *dev) struct gpio_device *gdev = dev_get_drvdata(dev); unsigned long flags; -+ deb_debug("\n"); ++ deb_verbose("\n"); + spin_lock_irqsave(&gpio_lock, flags); list_del(&gdev->list); spin_unlock_irqrestore(&gpio_lock, flags); -@@ -480,7 +520,7 @@ static void gpiodevice_release(struct device *dev) +@@ -480,7 +539,7 @@ static void gpiodevice_release(struct device *dev) kfree(gdev); } @@ -1466,7 +2044,7 @@ index 50abb1c20df0..9e466eeff297 100644 #define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt)) #define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev)) #else -@@ -490,13 +530,27 @@ static void gpiodevice_release(struct device *dev) +@@ -490,13 +549,66 @@ static void gpiodevice_release(struct device *dev) */ #define gcdev_register(gdev, devt) device_add(&(gdev)->dev) #define gcdev_unregister(gdev) device_del(&(gdev)->dev) @@ -1481,7 +2059,43 @@ index 50abb1c20df0..9e466eeff297 100644 { int ret; -+ deb_debug("\n"); ++ deb_verbose("\n"); ++ ++ // store GPIO char device for use by proxy host driver (In guest this is redundant) ++ if (gpio_dev_count == 2) { ++ pr_err("GPIO %s, error, found more than two devices -- file %s", __func__, __FILE__); ++ } ++ proxy_host_gpio_dev[gpio_dev_count++] = gdev; ++ // we continue to populate gdev ++ ++ ret = gcdev_register(gdev, gpio_devt); ++ ++ if (ret) ++ return ret; ++ ++ ret = gpiochip_sysfs_register(gdev); ++ if (ret) ++ goto err_remove_device; ++ ++ /* From this point, the .release() function cleans up gpio_device */ ++ gdev->dev.release = gpiodevice_release; ++ pr_info("%s: registered GPIOs %d to %d on %s\n", ++ dev_name(&gdev->dev), gdev->base, ++ gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic"); ++ ++ return 0; ++ ++err_remove_device: ++ gcdev_unregister(gdev); ++ return ret; ++} ++ ++/* redirecting function to allow guest VMto use it even if hardware is not present */ ++static int gpiochip_setup_dev__redirect(struct gpio_device *gdev) ++{ ++ int ret; ++ ++ deb_verbose("\n"); + + // store GPIO char device for use by proxy host driver (In guest this is redundant) + if (gpio_dev_count == 2) { @@ -1490,48 +2104,61 @@ index 50abb1c20df0..9e466eeff297 100644 + proxy_host_gpio_dev[gpio_dev_count++] = gdev; + // we continue to populate gdev + ++// FIXIT -- debug ++goto debug_end; ++ ret = gcdev_register(gdev, gpio_devt); + if (ret) return ret; -@@ -522,6 +576,8 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) +@@ -504,6 +616,9 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) + if (ret) + goto err_remove_device; + ++// FIXIT --debug ++debug_end: ++ + /* From this point, the .release() function cleans up gpio_device */ + gdev->dev.release = gpiodevice_release; + pr_info("%s: registered GPIOs %d to %d on %s\n", +@@ -522,6 +637,8 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) struct gpio_desc *desc; int rv; -+ deb_debug("\n"); ++ deb_verbose("\n"); + desc = gpiochip_get_desc(gc, hog->chip_hwnum); if (IS_ERR(desc)) { chip_err(gc, "%s: unable to get GPIO desc: %ld\n", __func__, -@@ -542,6 +598,8 @@ static void machine_gpiochip_add(struct gpio_chip *gc) +@@ -542,6 +659,8 @@ static void machine_gpiochip_add(struct gpio_chip *gc) { struct gpiod_hog *hog; -+ deb_debug("\n"); ++ deb_verbose("\n"); + mutex_lock(&gpio_machine_hogs_mutex); list_for_each_entry(hog, &gpio_machine_hogs, list) { -@@ -557,6 +615,8 @@ static void gpiochip_setup_devs(void) +@@ -557,6 +676,8 @@ static void gpiochip_setup_devs(void) struct gpio_device *gdev; int ret; -+ deb_debug("\n"); ++ deb_verbose("\n"); + list_for_each_entry(gdev, &gpio_devices, list) { ret = gpiochip_setup_dev(gdev); if (ret) -@@ -576,6 +636,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -576,6 +697,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = gc->base; struct gpio_device *gdev; -+ deb_debug("\n"); ++ deb_verbose("\n"); + /* * First: allocate and populate the internal stat container, and * set up the struct device. -@@ -591,13 +653,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -591,13 +714,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gdev->dev.of_node = gc->parent->of_node; } @@ -1547,7 +2174,7 @@ index 50abb1c20df0..9e466eeff297 100644 /* * Assign fwnode depending on the result of the previous calls, -@@ -689,9 +751,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -689,9 +812,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier); @@ -1559,41 +2186,312 @@ index 50abb1c20df0..9e466eeff297 100644 if (gc->names) ret = gpiochip_set_desc_names(gc); -@@ -802,6 +864,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); +@@ -793,6 +916,270 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, + } + EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); + ++/* redirect function added for gpio passthrough in Guest VM ++ * Virtual machine does not have psysical access to resources ++ */ ++ ++extern int of_gpiochip_add__redirect(struct gpio_chip *chip); ++extern int devm_gpiochip_add_data__redirect(struct device *dev, struct gpio_chip *gc, void *data); ++ ++int gpiochip_add_data__redirect(struct gpio_chip *gc, void *data) ++{ ++ struct lock_class_key *lock_key = NULL; ++ struct lock_class_key *request_key = NULL; ++ struct fwnode_handle *fwnode = gc->parent ? dev_fwnode(gc->parent) : NULL; ++ unsigned long flags; ++ int ret = 0; ++ unsigned i; ++ int base = gc->base; ++ struct gpio_device *gdev; ++ ++ deb_verbose("\n"); ++ ++ /* ++ * First: allocate and populate the internal stat container, and ++ * set up the struct device. ++ */ ++ gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); ++ if (!gdev) ++ return -ENOMEM; ++ gdev->dev.bus = &gpio_bus_type; ++ gdev->chip = gc; ++ gc->gpiodev = gdev; ++ if (gc->parent) { ++ gdev->dev.parent = gc->parent; ++ gdev->dev.of_node = gc->parent->of_node; ++ } ++ ++ #ifdef CONFIG_OF_GPIO ++ /* If the gpiochip has an assigned OF node this takes precedence */ ++ if (gc->of_node) ++ gdev->dev.of_node = gc->of_node; ++ else ++ gc->of_node = gdev->dev.of_node; ++ #endif ++ ++ /* ++ * Assign fwnode depending on the result of the previous calls, ++ * if none of them succeed, assign it to the parent's one. ++ */ ++ gdev->dev.fwnode = dev_fwnode(&gdev->dev) ?: fwnode; ++ ++ gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL); ++ if (gdev->id < 0) { ++ ret = gdev->id; ++ goto err_free_gdev; ++ } ++ ++ ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id); ++ if (ret) ++ goto err_free_ida; ++ ++ device_initialize(&gdev->dev); ++ dev_set_drvdata(&gdev->dev, gdev); ++ if (gc->parent && gc->parent->driver) ++ gdev->owner = gc->parent->driver->owner; ++ else if (gc->owner) ++ /* TODO: remove chip->owner */ ++ gdev->owner = gc->owner; ++ else ++ gdev->owner = THIS_MODULE; ++ ++ gdev->descs = kcalloc(gc->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL); ++ if (!gdev->descs) { ++ ret = -ENOMEM; ++ goto err_free_dev_name; ++ } ++ ++ if (gc->ngpio == 0) { ++ chip_err(gc, "tried to insert a GPIO chip with zero lines\n"); ++ ret = -EINVAL; ++ goto err_free_descs; ++ } ++ ++ if (gc->ngpio > FASTPATH_NGPIO) ++ chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n", ++ gc->ngpio, FASTPATH_NGPIO); ++ ++ gdev->label = kstrdup_const(gc->label ?: "unknown", GFP_KERNEL); ++ if (!gdev->label) { ++ ret = -ENOMEM; ++ goto err_free_descs; ++ } ++ ++ gdev->ngpio = gc->ngpio; ++ gdev->data = data; ++ ++ spin_lock_irqsave(&gpio_lock, flags); ++ ++ /* ++ * TODO: this allocates a Linux GPIO number base in the global ++ * GPIO numberspace for this chip. In the long run we want to ++ * get *rid* of this numberspace and use only descriptors, but ++ * it may be a pipe dream. It will not happen before we get rid ++ * of the sysfs interface anyways. ++ */ ++ if (base < 0) { ++ base = gpiochip_find_base(gc->ngpio); ++ if (base < 0) { ++ ret = base; ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ goto err_free_label; ++ } ++ ++ /* ++ * TODO: it should not be necessary to reflect the assigned ++ * base outside of the GPIO subsystem. Go over drivers and ++ * see if anyone makes use of this, else drop this and assign ++ * a poison instead. ++ */ ++ gc->base = base; ++ } ++ gdev->base = base; ++ ++ ++ ++//DEBUG ++ deb_verbose("precheck number of lines: %d, list=0x%llx, next=0x%llx, prev=0x%llx", gdev->ngpio, (long long unsigned int)&gdev->list, (long long unsigned int)gdev->list.next, (long long unsigned int)gdev->list.prev); ++ ++// debug gdev here --- empty list? ++ ret = gpiodev_add_to_list(gdev); ++ deb_verbose("number of lines: %d", gdev->ngpio); ++// XXX ++// TODO DEBUG ++// gpiodev_add_to_list returns empty list ++// Probably a BUG ++ if (ret) { ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ goto err_free_label; ++ } ++ ++ for (i = 0; i < gc->ngpio; i++) ++ gdev->descs[i].gdev = gdev; ++ ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ ++ BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier); ++ ++ #ifdef CONFIG_PINCTRL ++ INIT_LIST_HEAD(&gdev->pin_ranges); ++ #endif ++ ++ if (gc->names) ++ ret = gpiochip_set_desc_names(gc); ++ else ++ ret = devprop_gpiochip_set_names(gc); ++ if (ret) ++ goto err_remove_from_list; ++ ++ ret = gpiochip_alloc_valid_mask(gc); ++ if (ret) ++ goto err_remove_from_list; ++ ++ ret = of_gpiochip_add__redirect(gc); ++ if (ret) ++ goto err_free_gpiochip_mask; ++/* FIXIT -- debug ++ * skip this for debug ++ if (ret) ++ goto err_free_gpiochip_mask; ++ */ ++ ret = gpiochip_init_valid_mask(gc); ++ if (ret) ++ goto err_remove_of_chip; ++ ++ for (i = 0; i < gc->ngpio; i++) { ++ struct gpio_desc *desc = &gdev->descs[i]; ++ ++ if (gc->get_direction && gpiochip_line_is_valid(gc, i)) { ++ assign_bit(FLAG_IS_OUT, ++ &desc->flags, !gc->get_direction(gc, i)); ++ } else { ++ assign_bit(FLAG_IS_OUT, ++ &desc->flags, !gc->direction_input); ++ } ++ } ++ ++ ret = gpiochip_add_pin_ranges(gc); ++ if (ret) ++ goto err_remove_of_chip; ++ ++ acpi_gpiochip_add(gc); ++ ++ machine_gpiochip_add(gc); ++ ++ ret = gpiochip_irqchip_init_valid_mask(gc); ++ if (ret) ++ goto err_remove_acpi_chip; ++ ++ ret = gpiochip_irqchip_init_hw(gc); ++ if (ret) ++ goto err_remove_acpi_chip; ++ ++ ret = gpiochip_add_irqchip(gc, lock_key, request_key); ++ if (ret) ++ goto err_remove_irqchip_mask; ++ ++ /* ++ * By first adding the chardev, and then adding the device, ++ * we get a device node entry in sysfs under ++ * /sys/bus/gpio/devices/gpiochipN/dev that can be used for ++ * coldplug of device nodes and other udev business. ++ * We can do this only if gpiolib has been initialized. ++ * Otherwise, defer until later. ++ */ ++ if (gpiolib_initialized) { ++ ret = gpiochip_setup_dev__redirect(gdev); ++ if (ret) ++ goto err_remove_irqchip; ++ } ++ return 0; ++ ++err_remove_irqchip: ++deb_info("A"); ++ gpiochip_irqchip_remove(gc); ++err_remove_irqchip_mask: ++deb_info("B"); ++ gpiochip_irqchip_free_valid_mask(gc); ++err_remove_acpi_chip: ++deb_info("C"); ++ acpi_gpiochip_remove(gc); ++err_remove_of_chip: ++deb_info("D"); ++ gpiochip_free_hogs(gc); ++ of_gpiochip_remove(gc); ++err_free_gpiochip_mask: ++deb_info("E -- of_gpiochip_add__redirect fails"); ++ gpiochip_remove_pin_ranges(gc); ++ gpiochip_free_valid_mask(gc); ++err_remove_from_list: ++deb_info("F"); ++ spin_lock_irqsave(&gpio_lock, flags); ++ list_del(&gdev->list); ++ spin_unlock_irqrestore(&gpio_lock, flags); ++err_free_label: ++deb_info("G"); ++ kfree_const(gdev->label); ++err_free_descs: ++deb_info("H"); ++ kfree(gdev->descs); ++err_free_dev_name: ++deb_info("I"); ++ kfree(dev_name(&gdev->dev)); ++err_free_ida: ++deb_info("J"); ++ ida_free(&gpio_ida, gdev->id); ++err_free_gdev: ++deb_info("K"); ++ /* failures here can mean systems won't boot... */ ++ pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__, ++ gdev->base, gdev->base + gdev->ngpio - 1, ++ gc->label ? : "generic", ret); ++ kfree(gdev); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gpiochip_add_data__redirect); ++ + /** + * gpiochip_get_data() - get per-subdriver data for the chip + * @gc: GPIO chip +@@ -802,6 +1189,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); */ void *gpiochip_get_data(struct gpio_chip *gc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + return gc->gpiodev->data; } EXPORT_SYMBOL_GPL(gpiochip_get_data); -@@ -818,6 +882,8 @@ void gpiochip_remove(struct gpio_chip *gc) +@@ -818,6 +1207,8 @@ void gpiochip_remove(struct gpio_chip *gc) unsigned long flags; unsigned int i; -+ deb_debug("\n"); ++ deb_verbose("\n"); + /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ gpiochip_sysfs_unregister(gdev); gpiochip_free_hogs(gc); -@@ -875,6 +941,8 @@ struct gpio_chip *gpiochip_find(void *data, +@@ -875,6 +1266,8 @@ struct gpio_chip *gpiochip_find(void *data, struct gpio_chip *gc = NULL; unsigned long flags; -+ deb_debug("\n"); ++ deb_verbose("\n"); + spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(gdev, &gpio_devices, list) if (gdev->chip && match(gdev->chip, data)) { -@@ -895,12 +963,15 @@ static int gpiochip_match_name(struct gpio_chip *gc, void *data) +@@ -895,12 +1288,15 @@ static int gpiochip_match_name(struct gpio_chip *gc, void *data) return !strcmp(gc->label, name); } -static struct gpio_chip *find_chip_by_name(const char *name) +struct gpio_chip *find_chip_by_name(const char *name) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + return gpiochip_find((void *)name, gpiochip_match_name); } @@ -1604,52 +2502,52 @@ index 50abb1c20df0..9e466eeff297 100644 /* * The following is irqchip helper code for gpiochips. -@@ -910,6 +981,8 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc) +@@ -910,6 +1306,8 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc) { struct gpio_irq_chip *girq = &gc->irq; -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!girq->init_hw) return 0; -@@ -920,6 +993,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -920,6 +1318,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) { struct gpio_irq_chip *girq = &gc->irq; -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!girq->init_valid_mask) return 0; -@@ -934,6 +1009,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -934,6 +1334,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + bitmap_free(gc->irq.valid_mask); gc->irq.valid_mask = NULL; } -@@ -941,6 +1018,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) +@@ -941,6 +1343,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, unsigned int offset) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!gpiochip_line_is_valid(gc, offset)) return false; /* No mask means all valid */ -@@ -966,6 +1045,8 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, +@@ -966,6 +1370,8 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, struct gpio_irq_chip *girq = &gc->irq; struct device *dev = &gc->gpiodev->dev; -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!girq->domain) { chip_err(gc, "called %s before setting up irqchip\n", __func__); -@@ -1011,7 +1092,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gc, +@@ -1011,7 +1417,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gc, } EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); @@ -1658,25 +2556,25 @@ index 50abb1c20df0..9e466eeff297 100644 /** * gpiochip_set_hierarchical_irqchip() - connects a hierarchical irqchip -@@ -1250,6 +1331,8 @@ void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, +@@ -1250,6 +1656,8 @@ void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, { struct irq_fwspec *fwspec; -+ deb_debug("\n"); ++ deb_verbose("\n"); + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); if (!fwspec) return NULL; -@@ -1269,6 +1352,8 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, +@@ -1269,6 +1677,8 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, { struct irq_fwspec *fwspec; -+ deb_debug("\n"); ++ deb_verbose("\n"); + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); if (!fwspec) return NULL; -@@ -1296,7 +1381,7 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) +@@ -1296,7 +1706,7 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) return false; } @@ -1685,16 +2583,16 @@ index 50abb1c20df0..9e466eeff297 100644 /** * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip -@@ -1314,6 +1399,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, +@@ -1314,6 +1724,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, struct gpio_chip *gc = d->host_data; int ret = 0; -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!gpiochip_irqchip_irq_valid(gc, hwirq)) return -ENXIO; -@@ -1415,7 +1502,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) +@@ -1415,7 +1827,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) if (!gpiochip_irqchip_irq_valid(gc, offset)) return -ENXIO; @@ -1703,7 +2601,7 @@ index 50abb1c20df0..9e466eeff297 100644 if (irq_domain_is_hierarchy(domain)) { struct irq_fwspec spec; -@@ -1426,7 +1513,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) +@@ -1426,7 +1838,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) return irq_create_fwspec_mapping(&spec); } @@ -1712,7 +2610,7 @@ index 50abb1c20df0..9e466eeff297 100644 return irq_create_mapping(domain, offset); } -@@ -1709,7 +1796,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, +@@ -1709,7 +2121,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, } gc->irq.threaded = threaded; of_node = gc->parent->of_node; @@ -1721,7 +2619,7 @@ index 50abb1c20df0..9e466eeff297 100644 /* * If the gpiochip has an assigned OF node this takes precedence * FIXME: get rid of this and use gc->parent->of_node -@@ -1717,7 +1804,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, +@@ -1717,7 +2129,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, */ if (gc->of_node) of_node = gc->of_node; @@ -1730,7 +2628,7 @@ index 50abb1c20df0..9e466eeff297 100644 /* * Specifying a default trigger is a terrible idea if DT or ACPI is * used to configure the interrupts, as you may end-up with -@@ -1796,7 +1883,7 @@ static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -1796,7 +2208,7 @@ static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) { } @@ -1739,12 +2637,12 @@ index 50abb1c20df0..9e466eeff297 100644 /** * gpiochip_generic_request() - request the gpio function for a pin -@@ -1805,10 +1892,12 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) +@@ -1805,10 +2217,12 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) */ int gpiochip_generic_request(struct gpio_chip *gc, unsigned offset) { -#ifdef CONFIG_PINCTRL -+ deb_debug("chip %s, offset=%u\n", gc->label, offset); ++ deb_verbose("chip %s, offset=%u\n", gc->label, offset); + + #ifdef CONFIG_PINCTRL if (list_empty(&gc->gpiodev->pin_ranges)) @@ -1754,12 +2652,12 @@ index 50abb1c20df0..9e466eeff297 100644 return pinctrl_gpio_request(gc->gpiodev->base + offset); } -@@ -1821,10 +1910,12 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); +@@ -1821,10 +2235,12 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); */ void gpiochip_generic_free(struct gpio_chip *gc, unsigned offset) { -#ifdef CONFIG_PINCTRL -+ deb_debug("GPIO %s, chip %s, offset=%u\n", gc->label, offset); ++ deb_verbose("chip %s, offset=%u\n", gc->label, offset); + + #ifdef CONFIG_PINCTRL if (list_empty(&gc->gpiodev->pin_ranges)) @@ -1769,11 +2667,11 @@ index 50abb1c20df0..9e466eeff297 100644 pinctrl_gpio_free(gc->gpiodev->base + offset); } -@@ -1839,11 +1930,13 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free); +@@ -1839,11 +2255,13 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free); int gpiochip_generic_config(struct gpio_chip *gc, unsigned offset, unsigned long config) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + return pinctrl_gpio_set_config(gc->gpiodev->base + offset, config); } @@ -1784,34 +2682,34 @@ index 50abb1c20df0..9e466eeff297 100644 /** * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping -@@ -1865,6 +1958,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, +@@ -1865,6 +2283,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, struct gpio_device *gdev = gc->gpiodev; int ret; -+ deb_debug("\n"); ++ deb_verbose("\n"); + pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); if (!pin_range) { chip_err(gc, "failed to allocate pin ranges\n"); -@@ -1923,6 +2018,8 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, +@@ -1923,6 +2343,8 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, struct gpio_device *gdev = gc->gpiodev; int ret; -+ deb_debug("\n"); ++ deb_verbose("\n"); + pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); if (!pin_range) { chip_err(gc, "failed to allocate pin ranges\n"); -@@ -1964,6 +2061,8 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) +@@ -1964,6 +2386,8 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) struct gpio_pin_range *pin_range, *tmp; struct gpio_device *gdev = gc->gpiodev; -+ deb_debug("\n"); ++ deb_verbose("\n"); + list_for_each_entry_safe(pin_range, tmp, &gdev->pin_ranges, node) { list_del(&pin_range->node); pinctrl_remove_gpio_range(pin_range->pctldev, -@@ -1973,7 +2072,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) +@@ -1973,7 +2397,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) } EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); @@ -1820,25 +2718,25 @@ index 50abb1c20df0..9e466eeff297 100644 /* These "optional" allocation calls help prevent drivers from stomping * on each other, and help provide better diagnostics in debugfs. -@@ -1987,6 +2086,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) +@@ -1987,6 +2411,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) bool hogged = false; unsigned offset; -+ deb_debug("label=%s\n", label); ++ deb_verbose("label=%s\n", label); + if (label) { /* Free desc->label if already allocated. */ if (desc->label) { -@@ -2092,6 +2193,8 @@ int gpiod_request(struct gpio_desc *desc, const char *label) +@@ -2092,6 +2518,8 @@ int gpiod_request(struct gpio_desc *desc, const char *label) int ret = -EPROBE_DEFER; struct gpio_device *gdev; -+ deb_debug("label=%s\n", label); ++ deb_verbose("label=%s\n", label); + VALIDATE_DESC(desc); gdev = desc->gdev; -@@ -2108,6 +2211,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label) +@@ -2108,6 +2536,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label) return ret; } @@ -1846,16 +2744,16 @@ index 50abb1c20df0..9e466eeff297 100644 static bool gpiod_free_commit(struct gpio_desc *desc) { -@@ -2115,6 +2219,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2115,6 +2544,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) unsigned long flags; struct gpio_chip *gc; -+ deb_debug("\n"); ++ deb_verbose("\n"); + might_sleep(); gpiod_unexport(desc); -@@ -2141,12 +2247,12 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2141,12 +2572,12 @@ static bool gpiod_free_commit(struct gpio_desc *desc) clear_bit(FLAG_EDGE_RISING, &desc->flags); clear_bit(FLAG_EDGE_FALLING, &desc->flags); clear_bit(FLAG_IS_HOGGED, &desc->flags); @@ -1872,16 +2770,16 @@ index 50abb1c20df0..9e466eeff297 100644 ret = true; } -@@ -2159,6 +2265,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2159,6 +2590,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) void gpiod_free(struct gpio_desc *desc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (desc && desc->gdev && gpiod_free_commit(desc)) { module_put(desc->gdev->owner); put_device(&desc->gdev->dev); -@@ -2166,6 +2274,7 @@ void gpiod_free(struct gpio_desc *desc) +@@ -2166,6 +2599,7 @@ void gpiod_free(struct gpio_desc *desc) WARN_ON(extra_checks); } } @@ -1889,655 +2787,655 @@ index 50abb1c20df0..9e466eeff297 100644 /** * gpiochip_is_requested - return string iff signal was requested -@@ -2184,6 +2293,8 @@ const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned offset) +@@ -2184,6 +2618,8 @@ const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned offset) { struct gpio_desc *desc; -+ deb_debug("label=%s\n", gc->label); ++ deb_verbose("label=%s\n", gc->label); + if (offset >= gc->ngpio) return NULL; -@@ -2227,6 +2338,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, +@@ -2227,6 +2663,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, struct gpio_desc *desc = gpiochip_get_desc(gc, hwnum); int ret; -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (IS_ERR(desc)) { chip_err(gc, "failed to get GPIO descriptor\n"); return desc; -@@ -2274,6 +2387,8 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); +@@ -2274,6 +2712,8 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); static int gpio_do_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!gc->set_config) return -ENOTSUPP; -@@ -2286,6 +2401,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) +@@ -2286,6 +2726,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) unsigned long config; unsigned arg; -+ deb_debug("\n"); ++ deb_verbose("\n"); + switch (mode) { case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_UP: -@@ -2305,6 +2422,8 @@ static int gpio_set_bias(struct gpio_desc *desc) +@@ -2305,6 +2747,8 @@ static int gpio_set_bias(struct gpio_desc *desc) int bias = 0; int ret = 0; -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) bias = PIN_CONFIG_BIAS_DISABLE; else if (test_bit(FLAG_PULL_UP, &desc->flags)) -@@ -2334,6 +2453,8 @@ int gpiod_direction_input(struct gpio_desc *desc) +@@ -2334,6 +2778,8 @@ int gpiod_direction_input(struct gpio_desc *desc) struct gpio_chip *gc; int ret = 0; -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC(desc); gc = desc->gdev->chip; -@@ -2381,6 +2502,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) +@@ -2381,6 +2827,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) int val = !!value; int ret = 0; -+ deb_debug("\n"); ++ deb_verbose("\n"); + /* * It's OK not to specify .direction_output() if the gpiochip is * output-only, but if there is then not even a .set() operation it -@@ -2431,6 +2554,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) +@@ -2431,6 +2879,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) */ int gpiod_direction_output_raw(struct gpio_desc *desc, int value) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC(desc); return gpiod_direction_output_raw_commit(desc, value); } -@@ -2452,6 +2577,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) +@@ -2452,6 +2902,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) { int ret; -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC(desc); if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; -@@ -2523,6 +2650,8 @@ int gpiod_timestamp_control(struct gpio_desc *desc, int enable) +@@ -2523,6 +2975,8 @@ int gpiod_timestamp_control(struct gpio_desc *desc, int enable) { struct gpio_chip *chip; -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC(desc); chip = desc->gdev->chip; if (!chip->timestamp_control) { -@@ -2550,6 +2679,8 @@ int gpiod_timestamp_read(struct gpio_desc *desc, u64 *ts) +@@ -2550,6 +3004,8 @@ int gpiod_timestamp_read(struct gpio_desc *desc, u64 *ts) u64 gpio_ts; int ret; -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC(desc); chip = desc->gdev->chip; if (!chip->timestamp_read) { -@@ -2578,6 +2709,8 @@ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) +@@ -2578,6 +3034,8 @@ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) { struct gpio_chip *gc; -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC(desc); gc = desc->gdev->chip; -@@ -2598,6 +2731,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) +@@ -2598,6 +3056,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) { unsigned long config; -+ deb_debug("\n"); ++ deb_verbose("\n"); + config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); return gpiod_set_config(desc, config); } -@@ -2618,6 +2753,8 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) +@@ -2618,6 +3078,8 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) int gpio; int rc; -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC(desc); /* * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for -@@ -2652,6 +2789,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_transitory); +@@ -2652,6 +3114,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_transitory); */ int gpiod_is_active_low(const struct gpio_desc *desc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC(desc); return test_bit(FLAG_ACTIVE_LOW, &desc->flags); } -@@ -2663,6 +2802,8 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low); +@@ -2663,6 +3127,8 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low); */ void gpiod_toggle_active_low(struct gpio_desc *desc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC_VOID(desc); change_bit(FLAG_ACTIVE_LOW, &desc->flags); } -@@ -2696,6 +2837,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) +@@ -2696,6 +3162,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) int offset; int value; -+ deb_debug("\n"); ++ deb_verbose("\n"); + gc = desc->gdev->chip; offset = gpio_chip_hwgpio(desc); value = gc->get ? gc->get(gc, offset) : -EIO; -@@ -2707,6 +2850,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) +@@ -2707,6 +3175,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) static int gpio_chip_get_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (gc->get_multiple) { return gc->get_multiple(gc, mask, bits); } else if (gc->get) { -@@ -2731,6 +2876,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, +@@ -2731,6 +3201,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, { int ret, i = 0; -+ deb_debug("\n"); ++ deb_verbose("\n"); + /* * Validate array_info against desc_array and its size. * It should immediately follow desc_array if both -@@ -2837,6 +2984,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, +@@ -2837,6 +3309,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, */ int gpiod_get_raw_value(const struct gpio_desc *desc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC(desc); /* Should be using gpiod_get_raw_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -2858,6 +3007,8 @@ int gpiod_get_value(const struct gpio_desc *desc) +@@ -2858,6 +3332,8 @@ int gpiod_get_value(const struct gpio_desc *desc) { int value; -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC(desc); /* Should be using gpiod_get_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -2892,6 +3043,8 @@ int gpiod_get_raw_array_value(unsigned int array_size, +@@ -2892,6 +3368,8 @@ int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(true, false, array_size, -@@ -2918,6 +3071,8 @@ int gpiod_get_array_value(unsigned int array_size, +@@ -2918,6 +3396,8 @@ int gpiod_get_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(false, false, array_size, -@@ -2937,6 +3092,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) +@@ -2937,6 +3417,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) struct gpio_chip *gc = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (value) { ret = gc->direction_input(gc, offset); } else { -@@ -2962,6 +3119,8 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value +@@ -2962,6 +3444,8 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value struct gpio_chip *gc = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (value) { ret = gc->direction_output(gc, offset, 1); if (!ret) -@@ -2980,6 +3139,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) +@@ -2980,6 +3464,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) { struct gpio_chip *gc; -+ deb_debug("\n"); ++ deb_verbose("\n"); + gc = desc->gdev->chip; trace_gpio_value(desc_to_gpio(desc), 0, value); gc->set(gc, gpio_chip_hwgpio(desc), value); -@@ -2998,6 +3159,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) +@@ -2998,6 +3484,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) static void gpio_chip_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (gc->set_multiple) { gc->set_multiple(gc, mask, bits); } else { -@@ -3017,6 +3180,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, +@@ -3017,6 +3505,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, { int i = 0; -+ deb_debug("\n"); ++ deb_verbose("\n"); + /* * Validate array_info against desc_array and its size. * It should immediately follow desc_array if both -@@ -3122,6 +3287,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, +@@ -3122,6 +3612,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, */ void gpiod_set_raw_value(struct gpio_desc *desc, int value) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_raw_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -3140,6 +3307,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); +@@ -3140,6 +3632,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); */ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) -@@ -3163,6 +3332,8 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) +@@ -3163,6 +3657,8 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) */ void gpiod_set_value(struct gpio_desc *desc, int value) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -3188,6 +3359,8 @@ int gpiod_set_raw_array_value(unsigned int array_size, +@@ -3188,6 +3684,8 @@ int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(true, false, array_size, -@@ -3213,6 +3386,8 @@ int gpiod_set_array_value(unsigned int array_size, +@@ -3213,6 +3711,8 @@ int gpiod_set_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(false, false, array_size, -@@ -3228,6 +3403,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); +@@ -3228,6 +3728,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); */ int gpiod_cansleep(const struct gpio_desc *desc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC(desc); return desc->gdev->chip->can_sleep; } -@@ -3240,6 +3417,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); +@@ -3240,6 +3742,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); */ int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + VALIDATE_DESC(desc); if (name) { name = kstrdup_const(name, GFP_KERNEL); -@@ -3266,6 +3445,8 @@ int gpiod_to_irq(const struct gpio_desc *desc) +@@ -3266,6 +3770,8 @@ int gpiod_to_irq(const struct gpio_desc *desc) struct gpio_chip *gc; int offset; -+ deb_debug("\n"); ++ deb_verbose("\n"); + /* * Cannot VALIDATE_DESC() here as gpiod_to_irq() consumer semantics * requires this function to not return zero on an invalid descriptor -@@ -3300,6 +3481,8 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq); +@@ -3300,6 +3806,8 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq); int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc; + -+ deb_debug("\n"); ++ deb_verbose("\n"); desc = gpiochip_get_desc(gc, offset); if (IS_ERR(desc)) -@@ -3355,6 +3538,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3355,6 +3863,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc; -+ deb_debug("\n"); ++ deb_verbose("\n"); + desc = gpiochip_get_desc(gc, offset); if (IS_ERR(desc)) return; -@@ -3372,6 +3557,8 @@ void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3372,6 +3882,8 @@ void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc = gpiochip_get_desc(gc, offset); -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!IS_ERR(desc) && !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); -@@ -3382,6 +3569,8 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3382,6 +3894,8 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc = gpiochip_get_desc(gc, offset); -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!IS_ERR(desc) && !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) { /* -@@ -3397,6 +3586,8 @@ EXPORT_SYMBOL_GPL(gpiochip_enable_irq); +@@ -3397,6 +3911,8 @@ EXPORT_SYMBOL_GPL(gpiochip_enable_irq); bool gpiochip_line_is_irq(struct gpio_chip *gc, unsigned int offset) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (offset >= gc->ngpio) return false; -@@ -3430,6 +3621,8 @@ EXPORT_SYMBOL_GPL(gpiochip_relres_irq); +@@ -3430,6 +3946,8 @@ EXPORT_SYMBOL_GPL(gpiochip_relres_irq); bool gpiochip_line_is_open_drain(struct gpio_chip *gc, unsigned int offset) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (offset >= gc->ngpio) return false; -@@ -3439,6 +3632,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); +@@ -3439,6 +3957,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); bool gpiochip_line_is_open_source(struct gpio_chip *gc, unsigned int offset) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (offset >= gc->ngpio) return false; -@@ -3448,6 +3643,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); +@@ -3448,6 +3968,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); bool gpiochip_line_is_persistent(struct gpio_chip *gc, unsigned int offset) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (offset >= gc->ngpio) return false; -@@ -3466,6 +3663,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); +@@ -3466,6 +3988,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); */ int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + might_sleep_if(extra_checks); VALIDATE_DESC(desc); return gpiod_get_raw_value_commit(desc); -@@ -3485,6 +3684,8 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) +@@ -3485,6 +4009,8 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) { int value; -+ deb_debug("\n"); ++ deb_verbose("\n"); + might_sleep_if(extra_checks); VALIDATE_DESC(desc); value = gpiod_get_raw_value_commit(desc); -@@ -3516,6 +3717,8 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, +@@ -3516,6 +4042,8 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3542,6 +3745,8 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, +@@ -3542,6 +4070,8 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3563,6 +3768,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); +@@ -3563,6 +4093,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); */ void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + might_sleep_if(extra_checks); VALIDATE_DESC_VOID(desc); gpiod_set_raw_value_commit(desc, value); -@@ -3581,6 +3788,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); +@@ -3581,6 +4113,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); */ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + might_sleep_if(extra_checks); VALIDATE_DESC_VOID(desc); gpiod_set_value_nocheck(desc, value); -@@ -3604,6 +3813,8 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, +@@ -3604,6 +4138,8 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3621,6 +3832,8 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) +@@ -3621,6 +4157,8 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) { unsigned int i; -+ deb_debug("\n"); ++ deb_verbose("\n"); + mutex_lock(&gpio_lookup_lock); for (i = 0; i < n; i++) -@@ -3646,6 +3859,8 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, +@@ -3646,6 +4184,8 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3661,6 +3876,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); +@@ -3661,6 +4201,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); */ void gpiod_add_lookup_table(struct gpiod_lookup_table *table) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + mutex_lock(&gpio_lookup_lock); list_add_tail(&table->list, &gpio_lookup_list); -@@ -3675,6 +3892,8 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); +@@ -3675,6 +4217,8 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); */ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + mutex_lock(&gpio_lookup_lock); list_del(&table->list); -@@ -3692,6 +3911,8 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) +@@ -3692,6 +4236,8 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) struct gpio_chip *gc; struct gpiod_hog *hog; -+ deb_debug("\n"); ++ deb_verbose("\n"); + mutex_lock(&gpio_machine_hogs_mutex); for (hog = &hogs[0]; hog->chip_label; hog++) { -@@ -3715,6 +3936,8 @@ static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) +@@ -3715,6 +4261,8 @@ static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) const char *dev_id = dev ? dev_name(dev) : NULL; struct gpiod_lookup_table *table; -+ deb_debug("\n"); ++ deb_verbose("\n"); + mutex_lock(&gpio_lookup_lock); list_for_each_entry(table, &gpio_lookup_list, list) { -@@ -3748,6 +3971,8 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, +@@ -3748,6 +4296,8 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, struct gpiod_lookup_table *table; struct gpiod_lookup *p; -+ deb_debug("\n"); ++ deb_verbose("\n"); + table = gpiod_find_lookup_table(dev); if (!table) return desc; -@@ -3813,6 +4038,8 @@ static int platform_gpio_count(struct device *dev, const char *con_id) +@@ -3813,6 +4363,8 @@ static int platform_gpio_count(struct device *dev, const char *con_id) struct gpiod_lookup *p; unsigned int count = 0; -+ deb_debug("\n"); ++ deb_verbose("\n"); + table = gpiod_find_lookup_table(dev); if (!table) return -ENOENT; -@@ -3858,6 +4085,8 @@ struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, +@@ -3858,6 +4410,8 @@ struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, char prop_name[32]; /* 32 is max size of property name */ unsigned int i; -+ deb_debug("\n"); ++ deb_verbose("\n"); + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (con_id) snprintf(prop_name, sizeof(prop_name), "%s-%s", -@@ -3886,6 +4115,8 @@ int gpiod_count(struct device *dev, const char *con_id) +@@ -3886,6 +4440,8 @@ int gpiod_count(struct device *dev, const char *con_id) { int count = -ENOENT; -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) count = of_gpio_get_count(dev, con_id); else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) -@@ -3911,6 +4142,8 @@ EXPORT_SYMBOL_GPL(gpiod_count); +@@ -3911,6 +4467,8 @@ EXPORT_SYMBOL_GPL(gpiod_count); struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + return gpiod_get_index(dev, con_id, 0, flags); } EXPORT_SYMBOL_GPL(gpiod_get); -@@ -3951,6 +4184,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, +@@ -3951,6 +4509,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, { int ret; -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (lflags & GPIO_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); -@@ -4121,6 +4356,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, +@@ -4121,6 +4681,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, struct gpio_desc *desc = ERR_PTR(-ENODEV); int ret; -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (!fwnode) return ERR_PTR(-EINVAL); -@@ -4178,6 +4415,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, +@@ -4178,6 +4740,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, { struct gpio_desc *desc; -+ deb_debug("\n"); ++ deb_verbose("\n"); + desc = gpiod_get_index(dev, con_id, index, flags); if (IS_ERR(desc)) { if (PTR_ERR(desc) == -ENOENT) -@@ -4204,6 +4443,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, +@@ -4204,6 +4768,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, int hwnum; int ret; -+ deb_debug("\n"); ++ deb_verbose("\n"); + gc = gpiod_to_chip(desc); hwnum = gpio_chip_hwgpio(desc); -@@ -4235,6 +4476,8 @@ static void gpiochip_free_hogs(struct gpio_chip *gc) +@@ -4235,6 +4801,8 @@ static void gpiochip_free_hogs(struct gpio_chip *gc) { int id; -+ deb_debug("\n"); ++ deb_verbose("\n"); + for (id = 0; id < gc->ngpio; id++) { if (test_bit(FLAG_IS_HOGGED, &gc->gpiodev->descs[id].flags)) gpiochip_free_own_desc(&gc->gpiodev->descs[id]); -@@ -4263,6 +4506,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, +@@ -4263,6 +4831,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, struct gpio_chip *gc; int count, bitmap_size; -+ deb_debug("\n"); ++ deb_verbose("\n"); + count = gpiod_count(dev, con_id); if (count < 0) return ERR_PTR(count); -@@ -4383,6 +4628,8 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, +@@ -4383,6 +4953,8 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, { struct gpio_descs *descs; -+ deb_debug("\n"); ++ deb_verbose("\n"); + descs = gpiod_get_array(dev, con_id, flags); if (PTR_ERR(descs) == -ENOENT) return NULL; -@@ -4399,6 +4646,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); +@@ -4399,6 +4971,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); */ void gpiod_put(struct gpio_desc *desc) { -+ deb_debug("\n"); ++ deb_verbose("\n"); + if (desc) gpiod_free(desc); } -@@ -4412,6 +4661,8 @@ void gpiod_put_array(struct gpio_descs *descs) +@@ -4412,6 +4986,8 @@ void gpiod_put_array(struct gpio_descs *descs) { unsigned int i; -+ deb_debug("\n"); ++ deb_verbose("\n"); + for (i = 0; i < descs->ndescs; i++) gpiod_put(descs->desc[i]); -@@ -4423,6 +4674,8 @@ static int __init gpiolib_dev_init(void) +@@ -4423,6 +4999,8 @@ static int __init gpiolib_dev_init(void) { int ret; -+ deb_debug("\n"); ++ deb_verbose("\n"); + /* Register GPIO sysfs bus */ ret = bus_register(&gpio_bus_type); if (ret < 0) { -@@ -4442,13 +4695,13 @@ static int __init gpiolib_dev_init(void) +@@ -4442,13 +5020,13 @@ static int __init gpiolib_dev_init(void) #if IS_ENABLED(CONFIG_OF_DYNAMIC) && IS_ENABLED(CONFIG_OF_GPIO) WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier)); @@ -2553,14 +3451,14 @@ index 50abb1c20df0..9e466eeff297 100644 static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) { -@@ -4575,4 +4828,4 @@ static int __init gpiolib_debugfs_init(void) +@@ -4575,4 +5153,4 @@ static int __init gpiolib_debugfs_init(void) } subsys_initcall(gpiolib_debugfs_init); -#endif /* DEBUG_FS */ -+ #endif /* DEBUG_FS */ ++ #endif /* FIXIT -- debug_FS */ diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c -index c73b34e03aae..61fd6b71d8e0 100644 +index c73b34e03aae..4c46d7faac5e 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -26,16 +26,25 @@ @@ -2601,7 +3499,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (!devname) return NULL; -@@ -123,6 +135,9 @@ struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np) +@@ -123,16 +135,20 @@ struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np) { struct pinctrl_dev *pctldev; @@ -2611,7 +3509,19 @@ index c73b34e03aae..61fd6b71d8e0 100644 mutex_lock(&pinctrldev_list_mutex); list_for_each_entry(pctldev, &pinctrldev_list, node) -@@ -145,6 +160,8 @@ int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name) + if (pctldev->dev->of_node == np) { ++deb_debug("comparing %p and %p", pctldev->dev->of_node, np); // debug never prints this because we have an empty list? + mutex_unlock(&pinctrldev_list_mutex); + return pctldev; + } + + mutex_unlock(&pinctrldev_list_mutex); +- ++deb_debug("Error: failed to find match") + return NULL; + } + +@@ -145,6 +161,8 @@ int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name) { unsigned i, pin; @@ -2620,7 +3530,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 /* The pin number can be retrived from the pin controller descriptor */ for (i = 0; i < pctldev->desc->npins; i++) { struct pin_desc *desc; -@@ -168,6 +185,8 @@ const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin) +@@ -168,6 +186,8 @@ const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin) { const struct pin_desc *desc; @@ -2629,7 +3539,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 desc = pin_desc_get(pctldev, pin); if (!desc) { dev_err(pctldev->dev, "failed to get pin(%d) name\n", -@@ -186,6 +205,9 @@ static void pinctrl_free_pindescs(struct pinctrl_dev *pctldev, +@@ -186,6 +206,9 @@ static void pinctrl_free_pindescs(struct pinctrl_dev *pctldev, { int i; @@ -2639,7 +3549,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 for (i = 0; i < num_pins; i++) { struct pin_desc *pindesc; -@@ -206,6 +228,9 @@ static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev, +@@ -206,6 +229,9 @@ static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev, { struct pin_desc *pindesc; @@ -2649,7 +3559,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 pindesc = pin_desc_get(pctldev, pin->number); if (pindesc) { dev_err(pctldev->dev, "pin %d already registered\n", -@@ -247,6 +272,9 @@ static int pinctrl_register_pins(struct pinctrl_dev *pctldev, +@@ -247,6 +273,9 @@ static int pinctrl_register_pins(struct pinctrl_dev *pctldev, unsigned i; int ret = 0; @@ -2659,7 +3569,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 for (i = 0; i < num_descs; i++) { ret = pinctrl_register_one_pin(pctldev, &pins[i]); if (ret) -@@ -273,6 +301,9 @@ static inline int gpio_to_pin(struct pinctrl_gpio_range *range, +@@ -273,6 +302,9 @@ static inline int gpio_to_pin(struct pinctrl_gpio_range *range, unsigned int gpio) { unsigned int offset = gpio - range->base; @@ -2669,7 +3579,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (range->pins) return range->pins[offset]; else -@@ -292,6 +323,9 @@ pinctrl_match_gpio_range(struct pinctrl_dev *pctldev, unsigned gpio) +@@ -292,6 +324,9 @@ pinctrl_match_gpio_range(struct pinctrl_dev *pctldev, unsigned gpio) { struct pinctrl_gpio_range *range; @@ -2679,7 +3589,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 mutex_lock(&pctldev->mutex); /* Loop over the ranges */ list_for_each_entry(range, &pctldev->gpio_ranges, node) { -@@ -319,13 +353,16 @@ pinctrl_match_gpio_range(struct pinctrl_dev *pctldev, unsigned gpio) +@@ -319,13 +354,16 @@ pinctrl_match_gpio_range(struct pinctrl_dev *pctldev, unsigned gpio) * certain GPIO pin doesn't have back-end pinctrl device. If the return value * is false, it means that pinctrl device may not be ready. */ @@ -2697,7 +3607,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (WARN(!chip, "no gpio_chip for gpio%i?", gpio)) return false; -@@ -353,7 +390,7 @@ static bool pinctrl_ready_for_gpio_range(unsigned gpio) +@@ -353,7 +391,7 @@ static bool pinctrl_ready_for_gpio_range(unsigned gpio) } #else static bool pinctrl_ready_for_gpio_range(unsigned gpio) { return true; } @@ -2706,7 +3616,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 /** * pinctrl_get_device_gpio_range() - find device for GPIO range -@@ -372,6 +409,9 @@ static int pinctrl_get_device_gpio_range(unsigned gpio, +@@ -372,6 +410,9 @@ static int pinctrl_get_device_gpio_range(unsigned gpio, { struct pinctrl_dev *pctldev; @@ -2716,7 +3626,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 mutex_lock(&pinctrldev_list_mutex); /* Loop over the pin controllers */ -@@ -403,6 +443,9 @@ static int pinctrl_get_device_gpio_range(unsigned gpio, +@@ -403,6 +444,9 @@ static int pinctrl_get_device_gpio_range(unsigned gpio, void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range) { @@ -2726,7 +3636,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 mutex_lock(&pctldev->mutex); list_add_tail(&range->node, &pctldev->gpio_ranges); mutex_unlock(&pctldev->mutex); -@@ -415,6 +458,9 @@ void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, +@@ -415,6 +459,9 @@ void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, { int i; @@ -2736,7 +3646,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 for (i = 0; i < nranges; i++) pinctrl_add_gpio_range(pctldev, &ranges[i]); } -@@ -425,6 +471,9 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname, +@@ -425,6 +472,9 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname, { struct pinctrl_dev *pctldev; @@ -2746,7 +3656,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 pctldev = get_pinctrl_dev_from_devname(devname); /* -@@ -447,6 +496,9 @@ int pinctrl_get_group_pins(struct pinctrl_dev *pctldev, const char *pin_group, +@@ -447,6 +497,9 @@ int pinctrl_get_group_pins(struct pinctrl_dev *pctldev, const char *pin_group, const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; int gs; @@ -2756,7 +3666,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (!pctlops->get_group_pins) return -EINVAL; -@@ -464,6 +516,9 @@ pinctrl_find_gpio_range_from_pin_nolock(struct pinctrl_dev *pctldev, +@@ -464,6 +517,9 @@ pinctrl_find_gpio_range_from_pin_nolock(struct pinctrl_dev *pctldev, { struct pinctrl_gpio_range *range; @@ -2766,7 +3676,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 /* Loop over the ranges */ list_for_each_entry(range, &pctldev->gpio_ranges, node) { /* Check if we're in the valid range */ -@@ -493,6 +548,9 @@ pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev, +@@ -493,6 +549,9 @@ pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev, { struct pinctrl_gpio_range *range; @@ -2776,7 +3686,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 mutex_lock(&pctldev->mutex); range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin); mutex_unlock(&pctldev->mutex); -@@ -509,13 +567,16 @@ EXPORT_SYMBOL_GPL(pinctrl_find_gpio_range_from_pin); +@@ -509,13 +568,16 @@ EXPORT_SYMBOL_GPL(pinctrl_find_gpio_range_from_pin); void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range) { @@ -2794,7 +3704,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 /** * pinctrl_generic_get_group_count() - returns the number of pin groups -@@ -537,6 +598,9 @@ const char *pinctrl_generic_get_group_name(struct pinctrl_dev *pctldev, +@@ -537,6 +599,9 @@ const char *pinctrl_generic_get_group_name(struct pinctrl_dev *pctldev, { struct group_desc *group; @@ -2804,7 +3714,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 group = radix_tree_lookup(&pctldev->pin_group_tree, selector); if (!group) -@@ -560,6 +624,9 @@ int pinctrl_generic_get_group_pins(struct pinctrl_dev *pctldev, +@@ -560,6 +625,9 @@ int pinctrl_generic_get_group_pins(struct pinctrl_dev *pctldev, { struct group_desc *group; @@ -2814,7 +3724,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 group = radix_tree_lookup(&pctldev->pin_group_tree, selector); if (!group) { -@@ -585,6 +652,9 @@ struct group_desc *pinctrl_generic_get_group(struct pinctrl_dev *pctldev, +@@ -585,6 +653,9 @@ struct group_desc *pinctrl_generic_get_group(struct pinctrl_dev *pctldev, { struct group_desc *group; @@ -2824,7 +3734,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 group = radix_tree_lookup(&pctldev->pin_group_tree, selector); if (!group) -@@ -630,6 +700,9 @@ int pinctrl_generic_add_group(struct pinctrl_dev *pctldev, const char *name, +@@ -630,6 +701,9 @@ int pinctrl_generic_add_group(struct pinctrl_dev *pctldev, const char *name, struct group_desc *group; int selector; @@ -2834,7 +3744,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (!name) return -EINVAL; -@@ -668,6 +741,9 @@ int pinctrl_generic_remove_group(struct pinctrl_dev *pctldev, +@@ -668,6 +742,9 @@ int pinctrl_generic_remove_group(struct pinctrl_dev *pctldev, { struct group_desc *group; @@ -2844,7 +3754,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 group = radix_tree_lookup(&pctldev->pin_group_tree, selector); if (!group) -@@ -704,8 +780,8 @@ static void pinctrl_generic_free_groups(struct pinctrl_dev *pctldev) +@@ -704,8 +781,8 @@ static void pinctrl_generic_free_groups(struct pinctrl_dev *pctldev) static inline void pinctrl_generic_free_groups(struct pinctrl_dev *pctldev) { } @@ -2855,7 +3765,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 /** * pinctrl_get_group_selector() - returns the group selector for a group * @pctldev: the pin controller handling the group -@@ -718,6 +794,9 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, +@@ -718,6 +795,9 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, unsigned ngroups = pctlops->get_groups_count(pctldev); unsigned group_selector = 0; @@ -2865,7 +3775,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 while (group_selector < ngroups) { const char *gname = pctlops->get_group_name(pctldev, group_selector); -@@ -745,6 +824,9 @@ bool pinctrl_gpio_can_use_line(unsigned gpio) +@@ -745,6 +825,9 @@ bool pinctrl_gpio_can_use_line(unsigned gpio) bool result; int pin; @@ -2875,7 +3785,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 /* * Try to obtain GPIO range, if it fails * we're probably dealing with GPIO driver -@@ -781,6 +863,9 @@ int pinctrl_gpio_request(unsigned gpio) +@@ -781,6 +864,9 @@ int pinctrl_gpio_request(unsigned gpio) int ret; int pin; @@ -2885,7 +3795,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); if (ret) { if (pinctrl_ready_for_gpio_range(gpio)) -@@ -816,6 +901,8 @@ void pinctrl_gpio_free(unsigned gpio) +@@ -816,6 +902,8 @@ void pinctrl_gpio_free(unsigned gpio) int ret; int pin; @@ -2894,7 +3804,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); if (ret) { return; -@@ -838,6 +925,9 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input) +@@ -838,6 +926,9 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input) int ret; int pin; @@ -2904,7 +3814,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); if (ret) { return ret; -@@ -864,6 +954,9 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input) +@@ -864,6 +955,9 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input) */ int pinctrl_gpio_direction_input(unsigned gpio) { @@ -2914,7 +3824,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 return pinctrl_gpio_direction(gpio, true); } EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); -@@ -878,6 +971,9 @@ EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); +@@ -878,6 +972,9 @@ EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input); */ int pinctrl_gpio_direction_output(unsigned gpio) { @@ -2924,7 +3834,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 return pinctrl_gpio_direction(gpio, false); } EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_output); -@@ -898,6 +994,8 @@ int pinctrl_gpio_set_config(unsigned gpio, unsigned long config) +@@ -898,6 +995,8 @@ int pinctrl_gpio_set_config(unsigned gpio, unsigned long config) struct pinctrl_dev *pctldev; int ret, pin; @@ -2933,7 +3843,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); if (ret) return ret; -@@ -916,6 +1014,9 @@ static struct pinctrl_state *find_state(struct pinctrl *p, +@@ -916,6 +1015,9 @@ static struct pinctrl_state *find_state(struct pinctrl *p, { struct pinctrl_state *state; @@ -2943,7 +3853,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 list_for_each_entry(state, &p->states, node) if (!strcmp(state->name, name)) return state; -@@ -928,6 +1029,9 @@ static struct pinctrl_state *create_state(struct pinctrl *p, +@@ -928,6 +1030,9 @@ static struct pinctrl_state *create_state(struct pinctrl *p, { struct pinctrl_state *state; @@ -2953,7 +3863,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 state = kzalloc(sizeof(*state), GFP_KERNEL); if (!state) return ERR_PTR(-ENOMEM); -@@ -947,6 +1051,9 @@ static int add_setting(struct pinctrl *p, struct pinctrl_dev *pctldev, +@@ -947,6 +1052,9 @@ static int add_setting(struct pinctrl *p, struct pinctrl_dev *pctldev, struct pinctrl_setting *setting; int ret; @@ -2963,7 +3873,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 state = find_state(p, map->name); if (!state) state = create_state(p, map->name); -@@ -1009,6 +1116,9 @@ static struct pinctrl *find_pinctrl(struct device *dev) +@@ -1009,6 +1117,9 @@ static struct pinctrl *find_pinctrl(struct device *dev) { struct pinctrl *p; @@ -2973,7 +3883,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 mutex_lock(&pinctrl_list_mutex); list_for_each_entry(p, &pinctrl_list, node) if (p->dev == dev) { -@@ -1032,6 +1142,9 @@ static struct pinctrl *create_pinctrl(struct device *dev, +@@ -1032,6 +1143,9 @@ static struct pinctrl *create_pinctrl(struct device *dev, const struct pinctrl_map *map; int ret; @@ -2983,7 +3893,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 /* * create the state cookie holder struct pinctrl for each * mapping, this is what consumers will get when requesting -@@ -1115,6 +1228,9 @@ struct pinctrl *pinctrl_get(struct device *dev) +@@ -1115,6 +1229,9 @@ struct pinctrl *pinctrl_get(struct device *dev) { struct pinctrl *p; @@ -2993,7 +3903,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (WARN_ON(!dev)) return ERR_PTR(-EINVAL); -@@ -1137,6 +1253,9 @@ EXPORT_SYMBOL_GPL(pinctrl_get); +@@ -1137,6 +1254,9 @@ EXPORT_SYMBOL_GPL(pinctrl_get); static void pinctrl_free_setting(bool disable_setting, struct pinctrl_setting *setting) { @@ -3003,7 +3913,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 switch (setting->type) { case PIN_MAP_TYPE_MUX_GROUP: if (disable_setting) -@@ -1157,6 +1276,9 @@ static void pinctrl_free(struct pinctrl *p, bool inlist) +@@ -1157,6 +1277,9 @@ static void pinctrl_free(struct pinctrl *p, bool inlist) struct pinctrl_state *state, *n1; struct pinctrl_setting *setting, *n2; @@ -3013,7 +3923,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 mutex_lock(&pinctrl_list_mutex); list_for_each_entry_safe(state, n1, &p->states, node) { list_for_each_entry_safe(setting, n2, &state->settings, node) { -@@ -1184,6 +1306,9 @@ static void pinctrl_release(struct kref *kref) +@@ -1184,6 +1307,9 @@ static void pinctrl_release(struct kref *kref) { struct pinctrl *p = container_of(kref, struct pinctrl, users); @@ -3023,7 +3933,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 pinctrl_free(p, true); } -@@ -1193,6 +1318,9 @@ static void pinctrl_release(struct kref *kref) +@@ -1193,6 +1319,9 @@ static void pinctrl_release(struct kref *kref) */ void pinctrl_put(struct pinctrl *p) { @@ -3033,7 +3943,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 kref_put(&p->users, pinctrl_release); } EXPORT_SYMBOL_GPL(pinctrl_put); -@@ -1207,6 +1335,9 @@ struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, +@@ -1207,6 +1336,9 @@ struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, { struct pinctrl_state *state; @@ -3043,7 +3953,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 state = find_state(p, name); if (!state) { if (pinctrl_dummy_state) { -@@ -1225,6 +1356,9 @@ EXPORT_SYMBOL_GPL(pinctrl_lookup_state); +@@ -1225,6 +1357,9 @@ EXPORT_SYMBOL_GPL(pinctrl_lookup_state); static void pinctrl_link_add(struct pinctrl_dev *pctldev, struct device *consumer) { @@ -3053,7 +3963,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (pctldev->desc->link_consumers) device_link_add(consumer, pctldev->dev, DL_FLAG_PM_RUNTIME | -@@ -1242,6 +1376,9 @@ static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state) +@@ -1242,6 +1377,9 @@ static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state) struct pinctrl_state *old_state = p->state; int ret; @@ -3063,7 +3973,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (p->state) { /* * For each pinmux setting in the old state, forget SW's record -@@ -1289,6 +1426,8 @@ static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state) +@@ -1289,6 +1427,8 @@ static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state) unapply_new_state: dev_err(p->dev, "Error applying setting, reverse things back\n"); @@ -3072,7 +3982,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 list_for_each_entry(setting2, &state->settings, node) { if (&setting2->node == &setting->node) break; -@@ -1317,6 +1456,9 @@ static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state) +@@ -1317,6 +1457,9 @@ static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state) */ int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) { @@ -3082,7 +3992,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (p->state == state) return 0; -@@ -1340,6 +1482,9 @@ struct pinctrl *devm_pinctrl_get(struct device *dev) +@@ -1340,6 +1483,9 @@ struct pinctrl *devm_pinctrl_get(struct device *dev) { struct pinctrl **ptr, *p; @@ -3092,7 +4002,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 ptr = devres_alloc(devm_pinctrl_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return ERR_PTR(-ENOMEM); -@@ -1360,6 +1505,9 @@ static int devm_pinctrl_match(struct device *dev, void *res, void *data) +@@ -1360,6 +1506,9 @@ static int devm_pinctrl_match(struct device *dev, void *res, void *data) { struct pinctrl **p = res; @@ -3102,7 +4012,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 return *p == data; } -@@ -1373,6 +1521,9 @@ static int devm_pinctrl_match(struct device *dev, void *res, void *data) +@@ -1373,6 +1522,9 @@ static int devm_pinctrl_match(struct device *dev, void *res, void *data) */ void devm_pinctrl_put(struct pinctrl *p) { @@ -3112,7 +4022,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 WARN_ON(devres_release(p->dev, devm_pinctrl_release, devm_pinctrl_match, p)); } -@@ -1391,6 +1542,8 @@ int pinctrl_register_mappings(const struct pinctrl_map *maps, +@@ -1391,6 +1543,8 @@ int pinctrl_register_mappings(const struct pinctrl_map *maps, int i, ret; struct pinctrl_maps *maps_node; @@ -3121,7 +4031,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 pr_debug("add %u pinctrl maps\n", num_maps); /* First sanity check the new mapping */ -@@ -1459,6 +1612,8 @@ void pinctrl_unregister_mappings(const struct pinctrl_map *map) +@@ -1459,6 +1613,8 @@ void pinctrl_unregister_mappings(const struct pinctrl_map *map) { struct pinctrl_maps *maps_node; @@ -3130,7 +4040,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 mutex_lock(&pinctrl_maps_mutex); list_for_each_entry(maps_node, &pinctrl_maps, node) { if (maps_node->maps == map) { -@@ -1478,6 +1633,8 @@ EXPORT_SYMBOL_GPL(pinctrl_unregister_mappings); +@@ -1478,6 +1634,8 @@ EXPORT_SYMBOL_GPL(pinctrl_unregister_mappings); */ int pinctrl_force_sleep(struct pinctrl_dev *pctldev) { @@ -3139,7 +4049,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_sleep)) return pinctrl_commit_state(pctldev->p, pctldev->hog_sleep); return 0; -@@ -1490,6 +1647,8 @@ EXPORT_SYMBOL_GPL(pinctrl_force_sleep); +@@ -1490,6 +1648,8 @@ EXPORT_SYMBOL_GPL(pinctrl_force_sleep); */ int pinctrl_force_default(struct pinctrl_dev *pctldev) { @@ -3148,7 +4058,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_default)) return pinctrl_commit_state(pctldev->p, pctldev->hog_default); return 0; -@@ -1509,6 +1668,9 @@ int pinctrl_init_done(struct device *dev) +@@ -1509,6 +1669,9 @@ int pinctrl_init_done(struct device *dev) struct dev_pin_info *pins = dev->pins; int ret; @@ -3158,7 +4068,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (!pins) return 0; -@@ -1534,6 +1696,9 @@ static int pinctrl_select_bound_state(struct device *dev, +@@ -1534,6 +1697,9 @@ static int pinctrl_select_bound_state(struct device *dev, struct dev_pin_info *pins = dev->pins; int ret; @@ -3168,7 +4078,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (IS_ERR(state)) return 0; /* No such state */ ret = pinctrl_select_state(pins->p, state); -@@ -1549,6 +1714,9 @@ static int pinctrl_select_bound_state(struct device *dev, +@@ -1549,6 +1715,9 @@ static int pinctrl_select_bound_state(struct device *dev, */ int pinctrl_select_default_state(struct device *dev) { @@ -3178,7 +4088,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (!dev->pins) return 0; -@@ -1556,7 +1724,7 @@ int pinctrl_select_default_state(struct device *dev) +@@ -1556,7 +1725,7 @@ int pinctrl_select_default_state(struct device *dev) } EXPORT_SYMBOL_GPL(pinctrl_select_default_state); @@ -3187,7 +4097,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 /** * pinctrl_pm_select_default_state() - select default pinctrl state for PM -@@ -1564,6 +1732,9 @@ EXPORT_SYMBOL_GPL(pinctrl_select_default_state); +@@ -1564,6 +1733,9 @@ EXPORT_SYMBOL_GPL(pinctrl_select_default_state); */ int pinctrl_pm_select_default_state(struct device *dev) { @@ -3197,7 +4107,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 return pinctrl_select_default_state(dev); } EXPORT_SYMBOL_GPL(pinctrl_pm_select_default_state); -@@ -1587,26 +1758,32 @@ EXPORT_SYMBOL_GPL(pinctrl_pm_select_sleep_state); +@@ -1587,26 +1759,32 @@ EXPORT_SYMBOL_GPL(pinctrl_pm_select_sleep_state); */ int pinctrl_pm_select_idle_state(struct device *dev) { @@ -3234,7 +4144,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 seq_printf(s, "registered pins: %d\n", pctldev->desc->npins); -@@ -1624,7 +1801,7 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) +@@ -1624,7 +1802,7 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) seq_printf(s, "pin %d (%s) ", pin, desc->name); @@ -3243,7 +4153,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 gpio_num = -1; list_for_each_entry(range, &pctldev->gpio_ranges, node) { if ((pin >= range->pin_base) && -@@ -1641,7 +1818,7 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) +@@ -1641,7 +1819,7 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) seq_printf(s, "%u:%s ", gpio_num - chip->gpiodev->base, chip->label); else seq_puts(s, "0:? "); @@ -3252,7 +4162,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 /* Driver-specific info per pin */ if (ops->pin_dbg_show) -@@ -1662,6 +1839,9 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) +@@ -1662,6 +1840,9 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) const struct pinctrl_ops *ops = pctldev->desc->pctlops; unsigned ngroups, selector = 0; @@ -3262,7 +4172,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 mutex_lock(&pctldev->mutex); ngroups = ops->get_groups_count(pctldev); -@@ -1707,6 +1887,8 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what) +@@ -1707,6 +1888,8 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what) struct pinctrl_dev *pctldev = s->private; struct pinctrl_gpio_range *range; @@ -3271,7 +4181,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 seq_puts(s, "GPIO ranges handled:\n"); mutex_lock(&pctldev->mutex); -@@ -1740,6 +1922,8 @@ static int pinctrl_devices_show(struct seq_file *s, void *what) +@@ -1740,6 +1923,8 @@ static int pinctrl_devices_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev; @@ -3280,7 +4190,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 seq_puts(s, "name [pinmux] [pinconf]\n"); mutex_lock(&pinctrldev_list_mutex); -@@ -1773,6 +1957,8 @@ static inline const char *map_type(enum pinctrl_map_type type) +@@ -1773,6 +1958,8 @@ static inline const char *map_type(enum pinctrl_map_type type) "CONFIGS_GROUP", }; @@ -3289,7 +4199,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (type >= ARRAY_SIZE(names)) return "UNKNOWN"; -@@ -1785,6 +1971,8 @@ static int pinctrl_maps_show(struct seq_file *s, void *what) +@@ -1785,6 +1972,8 @@ static int pinctrl_maps_show(struct seq_file *s, void *what) int i; const struct pinctrl_map *map; @@ -3298,7 +4208,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 seq_puts(s, "Pinctrl maps:\n"); mutex_lock(&pinctrl_maps_mutex); -@@ -1823,6 +2011,8 @@ static int pinctrl_show(struct seq_file *s, void *what) +@@ -1823,6 +2012,8 @@ static int pinctrl_show(struct seq_file *s, void *what) struct pinctrl_state *state; struct pinctrl_setting *setting; @@ -3307,7 +4217,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 seq_puts(s, "Requested pin control handlers their pinmux maps:\n"); mutex_lock(&pinctrl_list_mutex); -@@ -1870,6 +2060,9 @@ static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) +@@ -1870,6 +2061,9 @@ static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) struct dentry *device_root; const char *debugfs_name; @@ -3317,7 +4227,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (pctldev->desc->name && strcmp(dev_name(pctldev->dev), pctldev->desc->name)) { debugfs_name = devm_kasprintf(pctldev->dev, GFP_KERNEL, -@@ -1906,11 +2099,15 @@ static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) +@@ -1906,11 +2100,15 @@ static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) static void pinctrl_remove_device_debugfs(struct pinctrl_dev *pctldev) { @@ -3333,7 +4243,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 debugfs_root = debugfs_create_dir("pinctrl", NULL); if (IS_ERR(debugfs_root) || !debugfs_root) { pr_warn("failed to create debugfs directory\n"); -@@ -1930,22 +2127,30 @@ static void pinctrl_init_debugfs(void) +@@ -1930,22 +2128,30 @@ static void pinctrl_init_debugfs(void) static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) { @@ -3366,7 +4276,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (!ops || !ops->get_groups_count || !ops->get_group_name) -@@ -1967,6 +2172,8 @@ pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev, +@@ -1967,6 +2173,8 @@ pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev, struct pinctrl_dev *pctldev; int ret; @@ -3375,7 +4285,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (!pctldesc) return ERR_PTR(-EINVAL); if (!pctldesc->name) -@@ -1981,12 +2188,12 @@ pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev, +@@ -1981,12 +2189,12 @@ pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev, pctldev->desc = pctldesc; pctldev->driver_data = driver_data; INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL); @@ -3392,7 +4302,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 INIT_LIST_HEAD(&pctldev->gpio_ranges); INIT_LIST_HEAD(&pctldev->node); pctldev->dev = dev; -@@ -2033,6 +2240,9 @@ pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev, +@@ -2033,6 +2241,9 @@ pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev, static int pinctrl_claim_hogs(struct pinctrl_dev *pctldev) { @@ -3402,7 +4312,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 pctldev->p = create_pinctrl(pctldev->dev, pctldev); if (PTR_ERR(pctldev->p) == -ENODEV) { dev_dbg(pctldev->dev, "no hogs found\n"); -@@ -2073,6 +2283,8 @@ int pinctrl_enable(struct pinctrl_dev *pctldev) +@@ -2073,6 +2284,8 @@ int pinctrl_enable(struct pinctrl_dev *pctldev) { int error; @@ -3411,7 +4321,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 error = pinctrl_claim_hogs(pctldev); if (error) { dev_err(pctldev->dev, "could not claim hogs: %i\n", -@@ -2112,6 +2324,8 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, +@@ -2112,6 +2325,8 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, struct pinctrl_dev *pctldev; int error; @@ -3420,7 +4330,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 pctldev = pinctrl_init_controller(pctldesc, dev, driver_data); if (IS_ERR(pctldev)) return pctldev; -@@ -2140,6 +2354,8 @@ int pinctrl_register_and_init(struct pinctrl_desc *pctldesc, +@@ -2140,6 +2355,8 @@ int pinctrl_register_and_init(struct pinctrl_desc *pctldesc, { struct pinctrl_dev *p; @@ -3429,7 +4339,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 p = pinctrl_init_controller(pctldesc, dev, driver_data); if (IS_ERR(p)) return PTR_ERR(p); -@@ -2166,6 +2382,8 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) +@@ -2166,6 +2383,8 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) { struct pinctrl_gpio_range *range, *n; @@ -3438,7 +4348,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (!pctldev) return; -@@ -2200,6 +2418,8 @@ static void devm_pinctrl_dev_release(struct device *dev, void *res) +@@ -2200,6 +2419,8 @@ static void devm_pinctrl_dev_release(struct device *dev, void *res) { struct pinctrl_dev *pctldev = *(struct pinctrl_dev **)res; @@ -3447,7 +4357,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 pinctrl_unregister(pctldev); } -@@ -2207,6 +2427,8 @@ static int devm_pinctrl_dev_match(struct device *dev, void *res, void *data) +@@ -2207,6 +2428,8 @@ static int devm_pinctrl_dev_match(struct device *dev, void *res, void *data) { struct pctldev **r = res; @@ -3456,7 +4366,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 if (WARN_ON(!r || !*r)) return 0; -@@ -2230,6 +2452,9 @@ struct pinctrl_dev *devm_pinctrl_register(struct device *dev, +@@ -2230,6 +2453,9 @@ struct pinctrl_dev *devm_pinctrl_register(struct device *dev, { struct pinctrl_dev **ptr, *pctldev; @@ -3466,7 +4376,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 ptr = devres_alloc(devm_pinctrl_dev_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return ERR_PTR(-ENOMEM); -@@ -2266,6 +2491,8 @@ int devm_pinctrl_register_and_init(struct device *dev, +@@ -2266,6 +2492,8 @@ int devm_pinctrl_register_and_init(struct device *dev, struct pinctrl_dev **ptr; int error; @@ -3475,7 +4385,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 ptr = devres_alloc(devm_pinctrl_dev_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return -ENOMEM; -@@ -2290,6 +2517,8 @@ EXPORT_SYMBOL_GPL(devm_pinctrl_register_and_init); +@@ -2290,6 +2518,8 @@ EXPORT_SYMBOL_GPL(devm_pinctrl_register_and_init); */ void devm_pinctrl_unregister(struct device *dev, struct pinctrl_dev *pctldev) { @@ -3484,7 +4394,7 @@ index c73b34e03aae..61fd6b71d8e0 100644 WARN_ON(devres_release(dev, devm_pinctrl_dev_release, devm_pinctrl_dev_match, pctldev)); } -@@ -2297,6 +2526,8 @@ EXPORT_SYMBOL_GPL(devm_pinctrl_unregister); +@@ -2297,6 +2527,8 @@ EXPORT_SYMBOL_GPL(devm_pinctrl_unregister); static int __init pinctrl_init(void) { @@ -3493,6 +4403,61 @@ index c73b34e03aae..61fd6b71d8e0 100644 pr_info("initialized pinctrl subsystem\n"); pinctrl_init_debugfs(); return 0; +diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c +index 3fb238714718..d48c607b9dab 100644 +--- a/drivers/pinctrl/devicetree.c ++++ b/drivers/pinctrl/devicetree.c +@@ -106,6 +106,12 @@ struct pinctrl_dev *of_pinctrl_get(struct device_node *np) + } + EXPORT_SYMBOL_GPL(of_pinctrl_get); + ++ ++// DEBUG ++#define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ ++ ++ + static int dt_to_map_one_config(struct pinctrl *p, + struct pinctrl_dev *hog_pctldev, + const char *statename, +@@ -151,6 +157,9 @@ static int dt_to_map_one_config(struct pinctrl *p, + } + of_node_put(np_pctldev); + ++// DEBUG ++deb_debug("# A #"); ++ + /* + * Call pinctrl driver to parse device tree node, and + * generate mapping table entries +@@ -161,7 +170,11 @@ static int dt_to_map_one_config(struct pinctrl *p, + dev_name(pctldev->dev)); + return -ENODEV; + } ++// DEBUG ++deb_debug("# B #"); + ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps); ++// DEBUG ++deb_debug("# C #"); + if (ret < 0) + return ret; + else if (num_maps == 0) { +@@ -172,11 +185,12 @@ static int dt_to_map_one_config(struct pinctrl *p, + */ + dev_info(p->dev, + "there is not valid maps for state %s\n", statename); ++// DEBUG ++deb_debug("# D #"); + return 0; + } +- +- /* Stash the mapping table chunk away for later use */ +- return dt_remember_or_free_map(p, statename, pctldev, map, num_maps); ++ /* Stash the mapping table chunk away for later use */ ++ return dt_remember_or_free_map(p, statename, pctldev, map, num_maps); + } + + static int dt_remember_dummy_state(struct pinctrl *p, const char *statename) diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c index f9ecbebe6442..f5541a687dc3 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index fa5728b26..23af0953e 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -15,7 +15,7 @@ index dcecc9f6e33f..ed7d58d68ba6 100644 endmenu diff --git a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile 2024-05-02 12:06:58.097355696 +0000 -+++ b/drivers/Makefile 2024-05-05 13:45:51.584426651 +0000 ++++ b/drivers/Makefile 2024-05-14 09:16:44.859711111 +0000 @@ -193,0 +194,5 @@ +# +# @@ -48,10 +48,10 @@ index 0000000..2580e02 +obj-$(CONFIG_TEGRA_GPIO_GUEST_PROXY) += gpio-guest-proxy.o diff --git a/drivers/gpio-guest-proxy/gpio-guest-proxy.c b/drivers/gpio-guest-proxy/gpio-guest-proxy.c new file mode 100644 -index 0000000..b42c834 +index 0000000..bca5ff4 --- /dev/null +++ b/drivers/gpio-guest-proxy/gpio-guest-proxy.c -@@ -0,0 +1,775 @@ +@@ -0,0 +1,795 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -139,18 +139,17 @@ index 0000000..b42c834 +{ + unsigned char *io_buffer; + -+ deb_debug("\n"); ++ // deb_debug("\n"); ++ #ifdef GPIO_DEBUG_VERBOSE ++ hexDump(DEVICE_NAME, "msg", &msg, msg_len); ++ deb_verbose("passthrough signal is: %c", *(char *)msg); ++ #endif + + // Copy msg, to io_buffer + io_buffer = kmalloc(msg_len, GFP_KERNEL); + memset(io_buffer, 0, msg_len); + memcpy(io_buffer, msg, msg_len); + -+ #ifdef GPIO_DEBUG_VERBOSE -+ hexDump(DEVICE_NAME, "msg", &msg, msg_len); -+ deb_verbose("msg signal is: %c\n", *(char *)msg); -+ #endif -+ + // Execute the request by copying the io_buffer + memcpy_toio(mem_iova, io_buffer, msg_len); + @@ -161,12 +160,38 @@ index 0000000..b42c834 + if(generic_return) { + // Copy reply to io_buffer + memcpy(generic_return, io_buffer, sizeof(*generic_return)); -+ deb_verbose("return value is copied %d\n", *generic_return); ++ deb_verbose("return value %d is copied", *generic_return); + } + + kfree(io_buffer); +} + ++// redirect static inline u32 readl(const volatile void __iomem *addr) ++inline u32 readl_redirect( void * addr) { ++ int ret = 0; ++ struct tegra_readl_writel msg; ++ ++ msg.signal = GPIO_READL; ++ msg.address = addr; ++ msg.value = 0; // value field is not used ++ ++ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ return (u32)ret; ++} ++EXPORT_SYMBOL_GPL(readl_redirect); ++ ++// redirect: static inline void writel(u32 value, volatile void __iomem *addr) ++inline void writel_redirect( u32 value, void * addr) { ++ struct tegra_readl_writel msg; ++ ++ msg.signal = GPIO_WRITEL; ++ msg.address = addr; ++ msg.value = value; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), NULL); ++} ++EXPORT_SYMBOL_GPL(writel_redirect); ++ +int gpiochip_generic_request_redirect(struct gpio_chip *chip, unsigned offset) { + int ret = 0; + struct tegra_gpio_pt msg; @@ -408,12 +433,11 @@ index 0000000..b42c834 + + deb_info("installing module."); + -+ deb_info("gpio_vpa: 0x%llx", gpio_vpa); -+ + if(!gpio_vpa){ + pr_err("Failed, gpio_vpa not defined"); ++ return -1; + } -+ ++ deb_info("gpio_vpa: 0x%llx", gpio_vpa); + + // Allocate a major number for the device. + major_number = register_chrdev(0, DEVICE_NAME, &fops); @@ -453,15 +477,11 @@ index 0000000..b42c834 + return -ENOMEM; + } + -+ deb_info("gpio_vpa: 0x%llX, mem_iova: %pFX\n", gpio_vpa, mem_iova); -+ -+ // gpio_hook is called by preserve_tegrachip() in gpio_tegra186.c -- don't call it here -+ // gpio_hook() ++ deb_info("mem_iova: 0x%llx\n", (long long unsigned int)mem_iova); + + is_set_up = true; + return 0; +} -+ +EXPORT_SYMBOL_GPL(tegra_gpio_guest_init); + +/* @@ -852,10 +872,10 @@ index 0000000..c2e0184 +obj-$(CONFIG_TEGRA_GPIO_HOST_PROXY) += gpio-host-proxy.o diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.c b/drivers/gpio-host-proxy/gpio-host-proxy.c new file mode 100644 -index 0000000..361804e +index 0000000..d67b2ab --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.c -@@ -0,0 +1,645 @@ +@@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -1170,6 +1190,7 @@ index 0000000..361804e + unsigned long int ret_l; + struct tegra_gpio_pt *kbuf = NULL; + tegra_gpio_pt_extended *kbuf_ext = NULL; ++ struct tegra_readl_writel *kbuf_rw = NULL; // used in special case the parameters are for readl and writel passthrough + + static struct file *file; + static struct inode *inode = NULL; @@ -1217,6 +1238,8 @@ index 0000000..361804e + } + deb_verbose("kbuf is set up, kbuf=%p", kbuf); + ++ // return_buffer += len; // ??? we should write return to the base address? no increment? generates new interrupt? ++ + if( len == (sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) ) { + kbuf_ext = (tegra_gpio_pt_extended *)(kbuf + 1); + deb_verbose("kbuf_ext is set up kbuf_ext=%p", kbuf_ext); @@ -1229,6 +1252,16 @@ index 0000000..361804e + deb_verbose("enter switch with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); + + switch (kbuf->signal) { ++ case GPIO_READL: ++ kbuf_rw = (struct tegra_readl_writel *)kbuf; ++ ret = (int)readl(kbuf_rw->address); ++ goto end; ++ break; ++ case GPIO_WRITEL: ++ kbuf_rw = (struct tegra_readl_writel *)kbuf; ++ writel(kbuf_rw->value, kbuf_rw->address); ++ goto retval; ++ break; + case GPIO_REQ: + // if(kbuf->chipnum & 0xfe) { // 0 and 1 are allowed values & mask allows fastcheck, marginal save + if(kbuf->chipnum >= MAX_CHIP) { // direct copmparison is more future flexible @@ -1503,10 +1536,10 @@ index 0000000..361804e +module_exit(gpio_host_proxy_exit); diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.h b/drivers/gpio-host-proxy/gpio-host-proxy.h new file mode 100644 -index 0000000..b714531 +index 0000000..1c98ec2 --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.h -@@ -0,0 +1,79 @@ +@@ -0,0 +1,88 @@ +#ifndef __GPIO_HOST_PROXY__H__ +#define __GPIO_HOST_PROXY__H__ + @@ -1546,7 +1579,7 @@ index 0000000..b714531 +#define GPIO_FREE 'f' // free + +#define GPIO_TIMESTAMP_CTRL 'C' // timestamp control -+#define GPIO_TIMESTAMP_READ 'R' // timestamp read ++#define GPIO_TIMESTAMP_READ 'T' // timestamp read +#define GPIO_SUSPEND_CONF 'S' // suspend configure +#define GPIO_ADD_PINRANGES 'P' // add_pin_ranges + @@ -1557,6 +1590,15 @@ index 0000000..b714531 +#define TEGRA_GPIO_AON_LABEL "tegra234-gpio-aon\x00\x00\x00" // gpio_aon_chip / gpiochip1 --padded to 20 bytes +#define LABEL_SIZE 20 + ++#define GPIO_READL 'R' ++#define GPIO_WRITEL 'W' ++ ++struct tegra_readl_writel { ++ unsigned char signal; ++ void * address; ++ u32 value; ++}; ++ +// struct __attribute__((packed)) tegra_gpio_pt { +struct tegra_gpio_pt { + unsigned char signal; // defines operation diff --git a/modules/microvm/virtualization/microvm/qemu-gpio-guestvm.dtb b/modules/microvm/virtualization/microvm/qemu-gpio-guestvm.dtb index cf556aa6aaef33e1a2ad820ad195f645854c9c8a..3293588cca1c8abf3678e8f96dccbb4d2d11b7b9 100644 GIT binary patch delta 597 zcmX@_+T^Bjf%o5A1_os&1_lNT1_p%(Kw1Nc1%X%qh=G7f2`Ii|qsC@INe&=)J4lR^ z2ShS312G>EAKLsta59tS8K@8okiG-NAVWb)K;Q)sgV-Rszd#I=Q($0V&dS~>#1TP81-&R{g0%p)TYbeoRMf5wiv^8tr>StR*~Dzcx3V&xxyv6Vv1v1!-V}RVZjPicemmaGI+uv#6vrF-Nx`adVzxA|n8% CfMD7H delta 563 zcmZp2JMXG-f%o5A1_q_`3=9kw3=9f8fV2h>3j(nK5CZ{|I8eM{qsC@IRz4ti`{pl# zlbI|J0-4M}%mTz0ftU%(2T}}op=^-cCm@E&DKIcFGEY7rQVlU^a)@X%S?0)q%@GhQ z*W_nlXaLy)atR1PUBoB~WS2nsLO|6DlQ)QYOR6OnB*hmb=VSwAKmexNXtId7IxmWk zCVPmdGg?mGATG`5v-ym8HxuKh$q`cBj8T)HNU4h^LG&~HM=~U5vVwFq9xJv;*DI=( zCKi=|?8o8|4xlrQCWpxUXRMtZA{)$$5{Q$x$a)J-0ouk0wGqUeKUqM|nsMD^AGz&} zJ14WqA7 Date: Tue, 14 May 2024 10:02:11 +0000 Subject: [PATCH 18/26] kernel patch for GPIO passthough is fixed --- .../patches/0004-gpio-virt-drivers.patch | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index 23af0953e..f541ef2c8 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -15,7 +15,7 @@ index dcecc9f6e33f..ed7d58d68ba6 100644 endmenu diff --git a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile 2024-05-02 12:06:58.097355696 +0000 -+++ b/drivers/Makefile 2024-05-14 09:16:44.859711111 +0000 ++++ b/drivers/Makefile 2024-05-14 10:00:52.054095256 +0000 @@ -193,0 +194,5 @@ +# +# @@ -872,10 +872,10 @@ index 0000000..c2e0184 +obj-$(CONFIG_TEGRA_GPIO_HOST_PROXY) += gpio-host-proxy.o diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.c b/drivers/gpio-host-proxy/gpio-host-proxy.c new file mode 100644 -index 0000000..d67b2ab +index 0000000..d61e894 --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.c -@@ -0,0 +1,658 @@ +@@ -0,0 +1,660 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -1211,7 +1211,9 @@ index 0000000..d67b2ab + } */ + + // We allow tegra_gpio_pt alone or with tegra_gpio_pt_extended (verify later) -+ if( len != sizeof(struct tegra_gpio_pt) && len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) { ++ if( len != sizeof(struct tegra_gpio_pt) && \ ++ len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) && \ ++ len != sizeof(struct tegra_readl_writel)) { + pr_err("Illegal chardev data length. Expected %ld or %ld, got %ld", sizeof(struct tegra_gpio_pt), sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), len); + return -ENOEXEC; + } @@ -1536,10 +1538,10 @@ index 0000000..d67b2ab +module_exit(gpio_host_proxy_exit); diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.h b/drivers/gpio-host-proxy/gpio-host-proxy.h new file mode 100644 -index 0000000..1c98ec2 +index 0000000..2996151 --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.h -@@ -0,0 +1,88 @@ +@@ -0,0 +1,93 @@ +#ifndef __GPIO_HOST_PROXY__H__ +#define __GPIO_HOST_PROXY__H__ + @@ -1593,10 +1595,12 @@ index 0000000..1c98ec2 +#define GPIO_READL 'R' +#define GPIO_WRITEL 'W' + ++// sizeof is rounded to even 64 bit passhtough writes -- no need to optimise size further on an aarch64 +struct tegra_readl_writel { -+ unsigned char signal; -+ void * address; ++ unsigned char signal; // Note: signal field is overloaded (based on field offset) with signal in struct tegra_gpio_pt ++ unsigned char pad[3]; // to get an even 64 bit message size + u32 value; ++ void * address; +}; + +// struct __attribute__((packed)) tegra_gpio_pt { @@ -1621,6 +1625,9 @@ index 0000000..1c98ec2 + +#define MAX_CHIP 2 + ++_Static_assert( sizeof(struct tegra_readl_writel) == 16, ++ "tegra_readl_writel size is not 16 bytes." ); ++ +_Static_assert( sizeof(struct tegra_gpio_pt) == 8, + "tegra_gpio_pt size is not 8 bytes." ); + From 64611e33f7b80f061fe5221467daad7be9eaeb47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Wed, 22 May 2024 13:29:15 +0000 Subject: [PATCH 19/26] readl/writel passthrough activated --- .../patches/0003-gpio-virt-kernel.patch | 885 +++++++++--------- .../patches/0004-gpio-virt-drivers.patch | 84 +- .../microvm/virtualization/microvm/netvm.nix | 3 +- targets/nvidia-jetson-orin/flake-module.nix | 6 +- 4 files changed, 508 insertions(+), 470 deletions(-) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch index 39e1aae3f..00a4c6e93 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch @@ -1,42 +1,101 @@ -diff --git a/drivers/gpio/gpio-irq-proxy.h b/drivers/gpio/gpio-irq-proxy.h +diff --git a/drivers/gpio/gpio-proxy.h b/drivers/gpio/gpio-proxy.h new file mode 100644 -index 000000000000..86ae48c95363 +index 000000000000..5cefb69d83ab --- /dev/null -+++ b/drivers/gpio/gpio-irq-proxy.h -@@ -0,0 +1,28 @@ -+#ifndef GPIO_IRQ_PROXY_H -+#define GPIO_IRQ_PROXY_H ++++ b/drivers/gpio/gpio-proxy.h +@@ -0,0 +1,87 @@ ++#ifndef GPIO_PROXY_H ++#define GPIO_PROXY_H + +/* passthrough hooks for low level functions sudh as readl adnn writel + * functions are mainly intended for GPIO passthrough + */ + -+ extern bool kernel_is_on_guest; -+ extern inline u32 readl_redirect( void * addr); -+ extern inline void writel_redirect( u32 value, void * addr); ++extern bool kernel_is_on_guest; ++extern inline u32 readl_redirect( void * addr, unsigned char type); ++extern inline void writel_redirect( u32 value, void * addr, unsigned char type); + -+ static inline u32 readl_x( void * addr) { -+ u32 ret; -+ if(kernel_is_on_guest) -+ ret = readl_redirect(addr); -+ else -+ ret = readl(addr); -+ return ret; -+ }; ++extern const unsigned char rwl_std_type; ++extern const unsigned char rwl_raw_type; ++extern const unsigned char rwl_relaxed_type; + -+ static inline void writel_x( u32 value, void * addr) { -+ if(kernel_is_on_guest) -+ writel_redirect(value, addr); -+ else -+ writel(value, addr); -+ }; ++static inline u32 readl_x( void * addr) { ++ u32 ret; ++ if(kernel_is_on_guest) { ++ ret = readl_redirect(addr, rwl_std_type); ++ } ++ else { ++ ret = readl(addr); ++ } ++ return ret; ++}; ++ ++static inline void writel_x( u32 value, void * addr) { ++ if(kernel_is_on_guest) { ++ writel_redirect(value, addr, rwl_std_type); ++ } ++ else { ++ writel(value, addr); ++ } ++}; ++ ++static inline u32 __raw_readl_x( void * addr) { ++ u32 ret; ++ if(kernel_is_on_guest) { ++ ret = readl_redirect(addr, rwl_raw_type); ++ } ++ else { ++ ret = __raw_readl(addr); ++ } ++ return ret; ++}; ++ ++static inline void __raw_writel_x( u32 value, void * addr) { ++ if(kernel_is_on_guest) { ++ writel_redirect(value, addr, rwl_raw_type); ++ } ++ else { ++ __raw_writel(value, addr); ++ } ++ ++}; ++static inline u32 readl_relaxed_x( void * addr) { ++ u32 ret; ++ if(kernel_is_on_guest) { ++ ret = readl_redirect(addr, rwl_relaxed_type); ++ } ++ else { ++ ret = readl_relaxed(addr); ++ } ++ return ret; ++}; ++ ++static inline void writel_relaxed_x( u32 value, void * addr) { ++ if(kernel_is_on_guest) { ++ writel_redirect(value, addr, rwl_relaxed_type); ++ } ++ else { ++ writel_relaxed(value, addr); ++ } ++}; ++ ++ ++// TODO, adding these passthroughs would make execution less latent ++static inline u32 pmx_readl_x( void * addr) { return 0; }; ++static inline void pmx_writel_x( u32 value, void * addr) {}; ++static inline u32 tegra_gpio_readl_x( void * addr) { return 0;}; ++static inline void tegra_gpio_writel_x( u32 value, void * addr) {}; ++static inline u32 tegra_gte_readl_x( void * addr) { return 0; }; ++static inline void tegra_gte_writel_x( u32 value, void * addr) {}; ++ ++// note: adding even higher level functions migth take latency off the lower level functions + +#endif diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c -index f66fc17faee4..629bc13aa4c3 100644 +index f66fc17faee4..01a24add5f6f 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c -@@ -58,13 +58,30 @@ +@@ -58,13 +58,34 @@ #define GPIO_INT_LVL_LEVEL_HIGH 0x000001 #define GPIO_INT_LVL_LEVEL_LOW 0x000000 @@ -56,6 +115,10 @@ index f66fc17faee4..629bc13aa4c3 100644 +#else + #define deb_verbose(fmt, ...) +#endif ++ ++#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++#include "gpio-proxy.h" // low level hooks for readl_x and writel_x ++#endif // CONFIG_TEGRA_GPIO_GUEST_PROXY and CONFIG_TEGRA_GPIO_HOST_PROXY + struct tegra_gpio_info; @@ -68,7 +131,7 @@ index f66fc17faee4..629bc13aa4c3 100644 u32 cnf[4]; u32 out[4]; u32 oe[4]; -@@ -72,7 +89,7 @@ struct tegra_gpio_bank { +@@ -72,7 +93,7 @@ struct tegra_gpio_bank { u32 int_lvl[4]; u32 wake_enb[4]; u32 dbc_enb[4]; @@ -77,20 +140,22 @@ index f66fc17faee4..629bc13aa4c3 100644 u32 dbc_cnt[4]; u32 cnf_init[4]; u32 out_init[4]; -@@ -101,17 +118,23 @@ static struct tegra_gpio_info *gpio_info; +@@ -101,17 +122,23 @@ static struct tegra_gpio_info *gpio_info; static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi, u32 val, u32 reg) { +- writel_relaxed(val, tgi->regs + reg); + deb_debug("\n"); + - writel_relaxed(val, tgi->regs + reg); ++ writel_relaxed_x(val, tgi->regs + reg); } static inline u32 tegra_gpio_readl(struct tegra_gpio_info *tgi, u32 reg) { +- return readl_relaxed(tgi->regs + reg); + deb_debug("\n"); + - return readl_relaxed(tgi->regs + reg); ++ return readl_relaxed_x(tgi->regs + reg); } static unsigned int tegra_gpio_compose(unsigned int bank, unsigned int port, @@ -101,7 +166,7 @@ index f66fc17faee4..629bc13aa4c3 100644 return (bank << 5) | ((port & 0x3) << 3) | (bit & 0x7); } -@@ -120,6 +143,8 @@ static void tegra_gpio_mask_write(struct tegra_gpio_info *tgi, u32 reg, +@@ -120,6 +147,8 @@ static void tegra_gpio_mask_write(struct tegra_gpio_info *tgi, u32 reg, { u32 val; @@ -110,7 +175,7 @@ index f66fc17faee4..629bc13aa4c3 100644 val = 0x100 << GPIO_BIT(gpio); if (value) val |= 1 << GPIO_BIT(gpio); -@@ -135,6 +160,8 @@ static void tegra_gpio_save_gpio_state(unsigned int gpio) +@@ -135,6 +164,8 @@ static void tegra_gpio_save_gpio_state(unsigned int gpio) u32 mask = BIT(GPIO_BIT(gpio)); unsigned long flags; @@ -119,7 +184,7 @@ index f66fc17faee4..629bc13aa4c3 100644 spin_lock_irqsave(&bank->gpio_lock[p], flags); bank->cnf_init[p] &= ~mask; -@@ -206,17 +233,23 @@ static void tegra_gpio_restore_gpio_state(unsigned int gpio) +@@ -206,17 +237,23 @@ static void tegra_gpio_restore_gpio_state(unsigned int gpio) static void tegra_gpio_enable(struct tegra_gpio_info *tgi, unsigned int gpio) { @@ -143,7 +208,7 @@ index f66fc17faee4..629bc13aa4c3 100644 pinctrl_gpio_free(chip->base + offset); tegra_gpio_restore_gpio_state(offset); } -@@ -226,6 +259,8 @@ static void tegra_gpio_set(struct gpio_chip *chip, unsigned int offset, +@@ -226,6 +263,8 @@ static void tegra_gpio_set(struct gpio_chip *chip, unsigned int offset, { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); @@ -152,7 +217,7 @@ index f66fc17faee4..629bc13aa4c3 100644 tegra_gpio_mask_write(tgi, GPIO_MSK_OUT(tgi, offset), offset, value); } -@@ -247,6 +282,8 @@ static int tegra_gpio_direction_input(struct gpio_chip *chip, +@@ -247,6 +286,8 @@ static int tegra_gpio_direction_input(struct gpio_chip *chip, struct tegra_gpio_info *tgi = gpiochip_get_data(chip); int ret; @@ -161,7 +226,7 @@ index f66fc17faee4..629bc13aa4c3 100644 tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0); tegra_gpio_enable(tgi, offset); -@@ -266,6 +303,8 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, +@@ -266,6 +307,8 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, struct tegra_gpio_info *tgi = gpiochip_get_data(chip); int ret; @@ -170,7 +235,7 @@ index f66fc17faee4..629bc13aa4c3 100644 tegra_gpio_set(chip, offset, value); tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1); tegra_gpio_enable(tgi, offset); -@@ -286,6 +325,8 @@ static int tegra_gpio_get_direction(struct gpio_chip *chip, +@@ -286,6 +329,8 @@ static int tegra_gpio_get_direction(struct gpio_chip *chip, u32 pin_mask = BIT(GPIO_BIT(offset)); u32 cnf, oe; @@ -179,7 +244,7 @@ index f66fc17faee4..629bc13aa4c3 100644 cnf = tegra_gpio_readl(tgi, GPIO_CNF(tgi, offset)); if (!(cnf & pin_mask)) return -EINVAL; -@@ -337,6 +378,8 @@ static int tegra_gpio_set_config(struct gpio_chip *chip, unsigned int offset, +@@ -337,6 +382,8 @@ static int tegra_gpio_set_config(struct gpio_chip *chip, unsigned int offset, { u32 debounce; @@ -188,7 +253,7 @@ index f66fc17faee4..629bc13aa4c3 100644 if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) return -ENOTSUPP; -@@ -348,6 +391,8 @@ static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) +@@ -348,6 +395,8 @@ static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); @@ -197,7 +262,7 @@ index f66fc17faee4..629bc13aa4c3 100644 return irq_find_mapping(tgi->irq_domain, offset); } -@@ -495,7 +540,7 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc) +@@ -495,7 +544,7 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc) } @@ -206,7 +271,7 @@ index f66fc17faee4..629bc13aa4c3 100644 static void tegra_gpio_resume(void) { struct tegra_gpio_info *tgi = gpio_info; -@@ -593,7 +638,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) +@@ -593,7 +642,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) #else #define tegra_gpio_suspend NULL #define tegra_gpio_resume NULL @@ -215,7 +280,7 @@ index f66fc17faee4..629bc13aa4c3 100644 static struct syscore_ops tegra_gpio_syscore_ops = { .suspend = tegra_gpio_suspend, -@@ -602,7 +647,7 @@ static struct syscore_ops tegra_gpio_syscore_ops = { +@@ -602,7 +651,7 @@ static struct syscore_ops tegra_gpio_syscore_ops = { .restore = tegra_gpio_resume, }; @@ -224,7 +289,7 @@ index f66fc17faee4..629bc13aa4c3 100644 #include #include -@@ -616,6 +661,8 @@ static int tegra_dbg_gpio_show(struct seq_file *s, void *unused) +@@ -616,6 +665,8 @@ static int tegra_dbg_gpio_show(struct seq_file *s, void *unused) x = ' '; y = 'A'; @@ -233,7 +298,7 @@ index f66fc17faee4..629bc13aa4c3 100644 seq_printf(s, "Name:Bank:Port CNF OE OUT IN INT_STA INT_ENB INT_LVL\n"); for (i = 0; i < tgi->bank_count; i++) { for (j = 0; j < 4; j++) { -@@ -649,6 +696,8 @@ DEFINE_SHOW_ATTRIBUTE(tegra_dbg_gpio); +@@ -649,6 +700,8 @@ DEFINE_SHOW_ATTRIBUTE(tegra_dbg_gpio); static void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) { @@ -242,7 +307,7 @@ index f66fc17faee4..629bc13aa4c3 100644 debugfs_create_file("tegra_gpio", 0444, NULL, tgi, &tegra_dbg_gpio_fops); } -@@ -659,7 +708,7 @@ static inline void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) +@@ -659,7 +712,7 @@ static inline void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) { } @@ -251,7 +316,7 @@ index f66fc17faee4..629bc13aa4c3 100644 static int tegra_gpio_probe(struct platform_device *pdev) { -@@ -668,6 +717,8 @@ static int tegra_gpio_probe(struct platform_device *pdev) +@@ -668,6 +721,8 @@ static int tegra_gpio_probe(struct platform_device *pdev) unsigned int gpio, i, j; int ret; @@ -260,7 +325,7 @@ index f66fc17faee4..629bc13aa4c3 100644 tgi = devm_kzalloc(&pdev->dev, sizeof(*tgi), GFP_KERNEL); if (!tgi) return -ENODEV; -@@ -707,9 +758,9 @@ static int tegra_gpio_probe(struct platform_device *pdev) +@@ -707,9 +762,9 @@ static int tegra_gpio_probe(struct platform_device *pdev) tgi->ic.irq_unmask = tegra_gpio_irq_unmask; tgi->ic.irq_set_type = tegra_gpio_irq_set_type; tgi->ic.irq_shutdown = tegra_gpio_irq_shutdown; @@ -272,7 +337,7 @@ index f66fc17faee4..629bc13aa4c3 100644 platform_set_drvdata(pdev, tgi); -@@ -816,6 +867,7 @@ static struct platform_driver tegra_gpio_driver = { +@@ -816,6 +871,7 @@ static struct platform_driver tegra_gpio_driver = { static int __init tegra_gpio_init(void) { @@ -281,22 +346,30 @@ index f66fc17faee4..629bc13aa4c3 100644 } subsys_initcall(tegra_gpio_init); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c -index 5e57824b283e..99bda33cadd4 100644 +index 5e57824b283e..f7835f80cd66 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c -@@ -44,7 +44,7 @@ - GPIO_SCR_SEC_REN) - - /* control registers */ --#define TEGRA186_GPIO_ENABLE_CONFIG 0x00 -+#define TEGRA186_GPIO_ENABLE_CONFIG 0x00 - #define TEGRA186_GPIO_ENABLE_CONFIG_ENABLE BIT(0) - #define TEGRA186_GPIO_ENABLE_CONFIG_OUT BIT(1) - #define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_NONE (0x0 << 2) -@@ -179,6 +179,55 @@ - - /**************************************************************/ +@@ -20,6 +20,46 @@ + #include + #include ++#define GPIO_DEBUG ++#define GPIO_DEBUG_VERBOSE ++ ++#ifdef GPIO_DEBUG ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++#else ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) ++#endif ++ ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ +// possibly/probably declare this in gpio-tegra.c instead +// following pattern from bpmp virtualisation +// @@ -305,10 +378,7 @@ index 5e57824b283e..99bda33cadd4 100644 + + #include "gpiolib.h" + #include -+ #include "gpio-irq-proxy.h" // low level hooks for readl_x and writel_x -+ -+ #define GPIO_DEBUG -+ #define GPIO_DEBUG_VERBOSE ++ #include "gpio-proxy.h" // low level hooks for readl and writel + + bool kernel_is_on_guest = false; + EXPORT_SYMBOL_GPL(kernel_is_on_guest); @@ -323,33 +393,10 @@ index 5e57824b283e..99bda33cadd4 100644 + +#endif + -+#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+#else -+ #define deb_info(fmt, ...) -+ #define deb_debug(fmt, ...) -+#endif -+ -+#ifdef GPIO_DEBUG_VERBOSE -+ #define deb_verbose deb_debug -+#else -+ #define deb_verbose(fmt, ...) -+#endif -+ -+/* this portion of code comes from copydrivers branch -+// structures to synchronise get_tegra186_gpio_driver() -+// it allows proxy drivers to copy preset gpio and driver to themselves -+static DECLARE_COMPLETION(gpio_data_ready); -+static DEFINE_SPINLOCK(gpio_data_lock); -+ -+static struct tegra_gpio preset_gpio_local[2]; -+*/ -+ - struct tegra_gpio_port { - const char *name; - unsigned int bank; -@@ -192,25 +241,25 @@ struct tegra186_pin_range { + /* security registers */ + #define TEGRA186_GPIO_CTL_SCR 0x0c + #define TEGRA186_GPIO_CTL_SCR_SEC_WEN BIT(28) +@@ -192,25 +232,25 @@ struct tegra186_pin_range { }; struct tegra_gpio_soc { @@ -391,24 +438,27 @@ index 5e57824b283e..99bda33cadd4 100644 }; struct tegra_gpio { -@@ -278,12 +327,16 @@ static struct tegra_gte_info tegra194_gte_info[] = { +@@ -278,13 +318,17 @@ static struct tegra_gte_info tegra194_gte_info[] = { static inline u32 tegra_gte_readl(struct tegra_gpio *tgi, u32 reg) { -+ // deb_verbose("\n"); +- return __raw_readl(tgi->gte_regs + reg); ++ deb_verbose("\n"); + - return __raw_readl(tgi->gte_regs + reg); ++ return __raw_readl_x(tgi->gte_regs + reg); } static inline void tegra_gte_writel(struct tegra_gpio *tgi, u32 reg, u32 val) { -+ // deb_verbose("\n"); +- __raw_writel(val, tgi->gte_regs + reg); ++ deb_verbose("\n"); + - __raw_writel(val, tgi->gte_regs + reg); ++ __raw_writel_x(val, tgi->gte_regs + reg); } -@@ -307,6 +360,8 @@ u64 tegra_gte_read_fifo(struct tegra_gpio *tgi, u32 offset) + static void tegra_gte_flush_fifo(struct tegra_gpio *tgi) +@@ -307,6 +351,8 @@ u64 tegra_gte_read_fifo(struct tegra_gpio *tgi, u32 offset) u32 aon_bits; u32 bit_index = 0; @@ -417,7 +467,7 @@ index 5e57824b283e..99bda33cadd4 100644 /* Check if FIFO is empty */ while ((tegra_gte_readl(tgi, GTE_GPIO_TESTATUS) >> GTE_GPIO_TESTATUS_OCCUPANCY_SHIFT) & -@@ -348,6 +403,8 @@ int tegra_gte_enable_ts(struct tegra_gpio *tgi, u32 offset) +@@ -348,6 +394,8 @@ int tegra_gte_enable_ts(struct tegra_gpio *tgi, u32 offset) u32 val, mask, reg; int i = 0; @@ -426,7 +476,7 @@ index 5e57824b283e..99bda33cadd4 100644 if (tgi->gte_enable == 1) { dev_err(tgi->gpio.parent, "timestamp is already enabled for gpio\n"); return -EINVAL; -@@ -381,6 +438,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) +@@ -381,6 +429,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) { u32 val, mask; @@ -435,7 +485,7 @@ index 5e57824b283e..99bda33cadd4 100644 if (tgi->gte_enable == 0) { dev_err(tgi->gpio.parent, "timestamp is already disabled\n"); return 0; -@@ -405,6 +464,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) +@@ -405,6 +455,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) int tegra_gte_setup(struct tegra_gpio *tgi) { @@ -444,7 +494,7 @@ index 5e57824b283e..99bda33cadd4 100644 tegra_gte_writel(tgi, GTE_GPIO_TECTRL, 0); tgi->gte_enable = 0; -@@ -418,6 +479,8 @@ tegra186_gpio_get_port(struct tegra_gpio *gpio, unsigned int *pin) +@@ -418,6 +470,8 @@ tegra186_gpio_get_port(struct tegra_gpio *gpio, unsigned int *pin) { unsigned int start = 0, i; @@ -453,7 +503,7 @@ index 5e57824b283e..99bda33cadd4 100644 for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; -@@ -438,6 +501,8 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, +@@ -438,6 +492,8 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, const struct tegra_gpio_port *port; unsigned int offset; @@ -462,7 +512,7 @@ index 5e57824b283e..99bda33cadd4 100644 port = tegra186_gpio_get_port(gpio, &pin); if (!port) return NULL; -@@ -453,6 +518,8 @@ static void __iomem *tegra186_gpio_get_secure(struct tegra_gpio *gpio, +@@ -453,6 +509,8 @@ static void __iomem *tegra186_gpio_get_secure(struct tegra_gpio *gpio, const struct tegra_gpio_port *port; unsigned int offset; @@ -471,7 +521,7 @@ index 5e57824b283e..99bda33cadd4 100644 port = tegra186_gpio_get_port(gpio, &pin); if (!port) return NULL; -@@ -466,14 +533,17 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) +@@ -466,14 +524,17 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) void __iomem *secure; u32 val; @@ -482,18 +532,18 @@ index 5e57824b283e..99bda33cadd4 100644 - val = __raw_readl(secure + GPIO_VM_REG); - if ((val & GPIO_VM_RW) != GPIO_VM_RW) - return false; -+ val = __raw_readl(secure + GPIO_VM_REG); ++ val = __raw_readl_x(secure + GPIO_VM_REG); + if ((val & GPIO_VM_RW) != GPIO_VM_RW) + return false; } - val = __raw_readl(secure + GPIO_SCR_REG); -+ val = __raw_readl(secure + GPIO_SCR_REG); ++ val = __raw_readl_x(secure + GPIO_SCR_REG); + // deb_verbose("val = 0x%X, val&mask = 0x%lX\n", val, (val & (GPIO_SCR_SEC_ENABLE))); if ((val & (GPIO_SCR_SEC_ENABLE)) == 0) return true; -@@ -484,13 +554,15 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) +@@ -484,13 +545,15 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) return false; } @@ -510,7 +560,7 @@ index 5e57824b283e..99bda33cadd4 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -498,14 +570,14 @@ static int tegra186_gpio_get_direction(struct gpio_chip *chip, +@@ -498,14 +561,15 @@ static int tegra186_gpio_get_direction(struct gpio_chip *chip, if (WARN_ON(base == NULL)) return -ENODEV; @@ -519,6 +569,7 @@ index 5e57824b283e..99bda33cadd4 100644 if (value & TEGRA186_GPIO_ENABLE_CONFIG_OUT) return GPIO_LINE_DIRECTION_OUT; ++ deb_verbose("OK\n"); return GPIO_LINE_DIRECTION_IN; } @@ -527,7 +578,7 @@ index 5e57824b283e..99bda33cadd4 100644 unsigned int offset) { struct tegra_gpio *gpio = gpiochip_get_data(chip); -@@ -513,6 +585,8 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, +@@ -513,6 +577,8 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, u32 value; int ret = 0; @@ -536,7 +587,7 @@ index 5e57824b283e..99bda33cadd4 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -520,14 +594,14 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, +@@ -520,14 +586,14 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, if (WARN_ON(base == NULL)) return -ENODEV; @@ -555,7 +606,7 @@ index 5e57824b283e..99bda33cadd4 100644 ret = pinctrl_gpio_direction_input(chip->base + offset); if (ret < 0) -@@ -536,7 +610,7 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, +@@ -536,7 +602,7 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, return ret; } @@ -564,7 +615,7 @@ index 5e57824b283e..99bda33cadd4 100644 unsigned int offset, int level) { struct tegra_gpio *gpio = gpiochip_get_data(chip); -@@ -544,6 +618,8 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, +@@ -544,6 +610,8 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, u32 value; int ret = 0; @@ -573,7 +624,7 @@ index 5e57824b283e..99bda33cadd4 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -555,14 +631,14 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, +@@ -555,14 +623,14 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, return -EINVAL; /* set the direction */ @@ -592,7 +643,7 @@ index 5e57824b283e..99bda33cadd4 100644 ret = pinctrl_gpio_direction_output(chip->base + offset); if (ret < 0) -@@ -578,6 +654,8 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, +@@ -578,6 +646,8 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, struct tegra_gpio_saved_register *regs; void __iomem *base; @@ -601,7 +652,7 @@ index 5e57824b283e..99bda33cadd4 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -586,9 +664,9 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, +@@ -586,9 +656,9 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, return -EINVAL; regs = &gpio->gpio_rval[offset]; @@ -614,7 +665,7 @@ index 5e57824b283e..99bda33cadd4 100644 regs->restore_needed = true; if (dflags & GPIOD_FLAGS_BIT_DIR_OUT) -@@ -606,14 +684,16 @@ static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, +@@ -606,14 +676,16 @@ static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, int value; int ret; @@ -629,11 +680,11 @@ index 5e57824b283e..99bda33cadd4 100644 + value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); value |= TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMP_FUNC; - writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); -+ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); ++ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); if (enable) ret = tegra_gte_enable_ts(gpio, offset); else -@@ -630,6 +710,8 @@ static int tegra_gpio_timestamp_read(struct gpio_chip *chip, unsigned offset, +@@ -630,6 +702,8 @@ static int tegra_gpio_timestamp_read(struct gpio_chip *chip, unsigned offset, struct tegra_gpio *tgi = gpiochip_get_data(chip); int ret; @@ -642,7 +693,7 @@ index 5e57824b283e..99bda33cadd4 100644 if (tgi->use_timestamp) { *ts = tegra_gte_read_fifo(tgi, offset); ret = 0; -@@ -645,40 +727,58 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) +@@ -645,40 +719,58 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) void __iomem *base; u32 value; @@ -709,7 +760,7 @@ index 5e57824b283e..99bda33cadd4 100644 } static int tegra186_gpio_set_config(struct gpio_chip *chip, -@@ -689,6 +789,8 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, +@@ -689,6 +781,8 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, u32 debounce, value; void __iomem *base; @@ -718,7 +769,7 @@ index 5e57824b283e..99bda33cadd4 100644 base = tegra186_gpio_get_base(gpio, offset); if (base == NULL) return -ENXIO; -@@ -708,11 +810,11 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, +@@ -708,11 +802,11 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, debounce = DIV_ROUND_UP(debounce, USEC_PER_MSEC); value = TEGRA186_GPIO_DEBOUNCE_CONTROL_THRESHOLD(debounce); @@ -733,7 +784,7 @@ index 5e57824b283e..99bda33cadd4 100644 return 0; } -@@ -725,6 +827,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) +@@ -725,6 +819,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) unsigned int i, j; int err; @@ -742,7 +793,7 @@ index 5e57824b283e..99bda33cadd4 100644 if (!gpio->soc->pinmux || gpio->soc->num_pin_ranges == 0) return 0; -@@ -768,6 +872,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, +@@ -768,6 +864,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, struct tegra_gpio *gpio = gpiochip_get_data(chip); unsigned int port, pin, i, offset = 0; @@ -751,7 +802,7 @@ index 5e57824b283e..99bda33cadd4 100644 if (WARN_ON(chip->of_gpio_n_cells < 2)) return -EINVAL; -@@ -803,7 +909,7 @@ static void tegra186_irq_ack(struct irq_data *data) +@@ -803,7 +901,7 @@ static void tegra186_irq_ack(struct irq_data *data) if (WARN_ON(base == NULL)) return; @@ -760,7 +811,7 @@ index 5e57824b283e..99bda33cadd4 100644 } static void tegra186_irq_mask(struct irq_data *data) -@@ -817,9 +923,9 @@ static void tegra186_irq_mask(struct irq_data *data) +@@ -817,9 +915,9 @@ static void tegra186_irq_mask(struct irq_data *data) if (WARN_ON(base == NULL)) return; @@ -772,7 +823,7 @@ index 5e57824b283e..99bda33cadd4 100644 } static void tegra186_irq_unmask(struct irq_data *data) -@@ -833,9 +939,9 @@ static void tegra186_irq_unmask(struct irq_data *data) +@@ -833,9 +931,9 @@ static void tegra186_irq_unmask(struct irq_data *data) if (WARN_ON(base == NULL)) return; @@ -784,7 +835,7 @@ index 5e57824b283e..99bda33cadd4 100644 } static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) -@@ -849,7 +955,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) +@@ -849,7 +947,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) if (WARN_ON(base == NULL)) return -ENODEV; @@ -793,7 +844,7 @@ index 5e57824b283e..99bda33cadd4 100644 value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK; value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL; -@@ -883,7 +989,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) +@@ -883,7 +981,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) return -EINVAL; } @@ -802,7 +853,7 @@ index 5e57824b283e..99bda33cadd4 100644 if ((type & IRQ_TYPE_EDGE_BOTH) == 0) irq_set_handler_locked(data, handle_level_irq); -@@ -930,7 +1036,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) +@@ -930,7 +1028,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) if (j == gpio->num_irqs_per_bank) goto skip; @@ -811,7 +862,7 @@ index 5e57824b283e..99bda33cadd4 100644 for_each_set_bit(pin, &value, port->pins) { irq = irq_find_mapping(domain, offset + pin); -@@ -1037,6 +1143,8 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1037,6 +1135,8 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) unsigned int i, j; u32 value; @@ -820,7 +871,7 @@ index 5e57824b283e..99bda33cadd4 100644 for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; unsigned int offset, p = port->port; -@@ -1044,7 +1152,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1044,7 +1144,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) base = gpio->secure + port->bank * 0x1000 + 0x800; @@ -829,7 +880,7 @@ index 5e57824b283e..99bda33cadd4 100644 /* * For controllers that haven't been locked down yet, make -@@ -1058,7 +1166,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1058,7 +1158,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) */ for (j = 0; j < gpio->num_irqs_per_bank; j++) { dev_dbg(dev, "programming default interrupt routing for port %s\n", @@ -838,7 +889,7 @@ index 5e57824b283e..99bda33cadd4 100644 offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j); -@@ -1073,9 +1181,9 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1073,9 +1173,9 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) */ if (j == 0) { @@ -850,7 +901,7 @@ index 5e57824b283e..99bda33cadd4 100644 } } } -@@ -1107,6 +1215,182 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +@@ -1107,6 +1207,182 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) return -EINVAL; } @@ -1013,13 +1064,13 @@ index 5e57824b283e..99bda33cadd4 100644 + /* find_chip_by_id + * replacement for find_chip_by_name, because it is slightly faster + * one doubtful assumption is that chip pointers are numbered by the driver -+ * in the same order preserve_tegrachip recirds them */ ++ * in the same order preserve_tegrachip records them */ + inline struct gpio_chip * find_chip_by_id(int id) { + int i = 0; + while (atomic_read(&tegra_gpio_hosts_ready) != MAX_CHIP) { + msleep(100); // Sleep briefly instead of looping infinitely. + if( i++ > 120 ) { -+ pr_err("GPIO tegra_gpio_hosts setup error\n"); ++ pr_err("GPIO tegra_gpio_hosts setup error: id=%d, count=%d\n", id, atomic_read(&tegra_gpio_hosts_ready)); + return NULL; + } + } @@ -1033,7 +1084,7 @@ index 5e57824b283e..99bda33cadd4 100644 static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; -@@ -1120,17 +1404,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1120,17 +1396,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) int value; void __iomem *base; @@ -1078,7 +1129,7 @@ index 5e57824b283e..99bda33cadd4 100644 /* count the number of banks in the controller */ for (i = 0; i < gpio->soc->num_ports; i++) -@@ -1139,11 +1446,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1139,11 +1438,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->num_banks++; @@ -1095,7 +1146,7 @@ index 5e57824b283e..99bda33cadd4 100644 sizeof(*gpio->gpio_rval), GFP_KERNEL); if (!gpio->gpio_rval) return -ENOMEM; -@@ -1154,9 +1465,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1154,9 +1457,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -EINVAL; } @@ -1108,7 +1159,7 @@ index 5e57824b283e..99bda33cadd4 100644 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gte"); if (!res) { dev_err(&pdev->dev, "Missing gte MEM resource\n"); -@@ -1172,14 +1486,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1172,14 +1478,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) } err = platform_irq_count(pdev); @@ -1132,7 +1183,7 @@ index 5e57824b283e..99bda33cadd4 100644 gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), GFP_KERNEL); -@@ -1188,27 +1508,44 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1188,27 +1500,43 @@ static int tegra186_gpio_probe(struct platform_device *pdev) for (i = 0; i < gpio->num_irq; i++) { err = platform_get_irq(pdev, i); @@ -1166,8 +1217,8 @@ index 5e57824b283e..99bda33cadd4 100644 + if (gpio->use_timestamp) + tegra_gte_setup(gpio); + -+ deb_debug("GPIO Guest init section\n"); + if( kernel_is_on_guest ) { ++ deb_debug("GPIO Guest init section\n"); + if( ! guest_proxy_is_set_up ) { + ret = tegra_gpio_guest_init(); + guest_proxy_is_set_up = true; @@ -1179,11 +1230,10 @@ index 5e57824b283e..99bda33cadd4 100644 + // gpio_unhook is the same as standard settings + // unhooked pointers are for the host driver on host only + BUG_ON(gpio_vpa != 0); // assert we do not set up the vpa driver -+ gpio_unhook(gpio); ++ gpio_unhook(gpio); // set standard function pointers + } -+ gpio->gpio.base = -1; -+ deb_debug("gpio function pointers are set for gpio label=%s\n", gpio->gpio.label); -+ preserve_tegrachip(gpio); ++ gpio->gpio.base = -1; ++ deb_debug("gpio function pointers are set for gpio label=%s\n", gpio->gpio.label); + #else + // this code segment is for standard operation in a compile without PROXY configuraton + deb_debug("Setting standard gpio functions for a non-proxy compile of driver\n") @@ -1193,7 +1243,7 @@ index 5e57824b283e..99bda33cadd4 100644 for (i = 0; i < gpio->soc->num_ports; i++) gpio->gpio.ngpio += gpio->soc->ports[i].pins; -@@ -1229,6 +1566,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1229,6 +1557,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -ENOMEM; names[offset + j] = name; @@ -1201,7 +1251,7 @@ index 5e57824b283e..99bda33cadd4 100644 } offset += port->pins; -@@ -1260,6 +1598,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1260,6 +1589,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parent_handler_data = gpio; irq->num_parents = gpio->num_irq; @@ -1210,7 +1260,7 @@ index 5e57824b283e..99bda33cadd4 100644 /* * To simplify things, use a single interrupt per bank for now. Some -@@ -1283,8 +1623,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1283,8 +1614,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parents = gpio->irq; } @@ -1226,7 +1276,7 @@ index 5e57824b283e..99bda33cadd4 100644 np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (!of_device_is_available(np)) -@@ -1315,9 +1662,24 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1315,9 +1653,24 @@ static int tegra186_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, gpio); @@ -1254,7 +1304,7 @@ index 5e57824b283e..99bda33cadd4 100644 if (gpio->soc->is_hw_ts_sup) { for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) { -@@ -1329,24 +1691,24 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1329,24 +1682,25 @@ static int tegra186_gpio_probe(struct platform_device *pdev) if (WARN_ON(base == NULL)) return -EINVAL; @@ -1276,6 +1326,7 @@ index 5e57824b283e..99bda33cadd4 100644 - - return 0; + #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) ++ preserve_tegrachip(gpio); + guest_skip: + #endif + return 0; @@ -1286,20 +1337,20 @@ index 5e57824b283e..99bda33cadd4 100644 static int tegra_gpio_resume_early(struct device *dev) { struct tegra_gpio *gpio = dev_get_drvdata(dev); -@@ -1366,9 +1728,9 @@ static int tegra_gpio_resume_early(struct device *dev) +@@ -1366,9 +1720,9 @@ static int tegra_gpio_resume_early(struct device *dev) regs->restore_needed = false; - writel(regs->val, base + TEGRA186_GPIO_OUTPUT_VALUE); - writel(regs->out, base + TEGRA186_GPIO_OUTPUT_CONTROL); - writel(regs->conf, base + TEGRA186_GPIO_ENABLE_CONFIG); -+ writel_x(regs->val, base + TEGRA186_GPIO_OUTPUT_VALUE); -+ writel_x(regs->out, base + TEGRA186_GPIO_OUTPUT_CONTROL); -+ writel_x(regs->conf, base + TEGRA186_GPIO_ENABLE_CONFIG); ++ writel_x(regs->val, base + TEGRA186_GPIO_OUTPUT_VALUE); ++ writel_x(regs->out, base + TEGRA186_GPIO_OUTPUT_CONTROL); ++ writel_x(regs->conf, base + TEGRA186_GPIO_ENABLE_CONFIG); } return 0; -@@ -1723,7 +2085,9 @@ static struct platform_driver tegra186_gpio_driver = { +@@ -1723,7 +2077,9 @@ static struct platform_driver tegra186_gpio_driver = { .probe = tegra186_gpio_probe, .remove = tegra186_gpio_remove, }; @@ -1613,22 +1664,16 @@ index 30e2476a6dc4..aab40f1cd52f 100644 err = gpio_request_one(array->gpio, array->flags, array->label); if (err) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c -index 647e77db82b1..2134815919de 100644 +index 647e77db82b1..994b57b11c0e 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c -@@ -24,6 +24,29 @@ +@@ -24,6 +24,27 @@ #include "gpiolib.h" #include "gpiolib-of.h" -+#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) -+ -+#include "gpio-irq-proxy.h" // low level hooks for readl_x and writel_x -+ +#define GPIO_DEBUG +#define GPIO_DEBUG_VERBOSE + -+#endif // CONFIG_TEGRA_GPIO_GUEST_PROXY and CONFIG_TEGRA_GPIO_HOST_PROXY -+ +#ifdef GPIO_DEBUG + #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) + #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) @@ -1642,97 +1687,23 @@ index 647e77db82b1..2134815919de 100644 +#else + #define deb_verbose(fmt, ...) +#endif ++ ++#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++#include "gpio-proxy.h" // low level hooks for readl_x and writel_x ++#endif // CONFIG_TEGRA_GPIO_GUEST_PROXY and CONFIG_TEGRA_GPIO_HOST_PROXY + /** * of_gpio_spi_cs_get_count() - special GPIO counting for SPI * @dev: Consuming device -@@ -1044,6 +1067,9 @@ static void of_gpiochip_init_valid_mask(struct gpio_chip *chip) +@@ -1141,7 +1162,6 @@ int of_gpiochip_add(struct gpio_chip *chip) + chip->of_gpio_n_cells = 2; + chip->of_xlate = of_gpio_simple_xlate; } - }; - -+// debug -+#include <../drivers/pinctrl/core.h> -+ - #ifdef CONFIG_PINCTRL - static int of_gpiochip_add_pin_range(struct gpio_chip *chip) - { -@@ -1066,12 +1092,34 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) - if (ret) - break; - -- pctldev = of_pinctrl_get(pinspec.np); -+// DEBUG -+if(pinspec.args_count == 0) deb_debug("no pins found in DT based on gpio-ranges"); -+deb_debug("node pointers, in=0x%llx, out=i0x%llx", (long long unsigned int)np, (long long unsigned int)pinspec.np); -+for(index=0; index < pinspec.args_count; index++) -+deb_debug("node index: %d, value: %d", index, pinspec.args[index]); -+ -+// FIXIT -- removed for debug -+// FIXIT -+// FIXIT -+// FIXIT -- we need this function to set pinranges ... a lot of pin data can be dummy -+// FIXIT -+// FIXIT -+// pctldev = kmalloc(sizeof(struct pinctrl_dev), GFP_KERNEL); // kmalloc is for debug use -+// memset(pctldev, 0, sizeof(struct pinctrl_dev)); // menset is a debug -+deb_verbose("np we search is: 0x%llx", (long long unsigned int)pinspec.np); // the BUG? np we search is: (____ptrval____) -+ pctldev = of_pinctrl_get(pinspec.np); - of_node_put(pinspec.np); -+//FIXIT - debug -+ - if (!pctldev) -+//FIXIT - debug -+{ deb_verbose("**A"); - return -EPROBE_DEFER; -+} -+deb_verbose("**a"); - - if (pinspec.args[2]) { -+deb_verbose("**b"); - if (group_names) { - of_property_read_string_index(np, - group_names_propname, -@@ -1082,6 +1130,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) - break; - } - } -+deb_verbose("**c"); - /* npins != 0: linear range */ - ret = gpiochip_add_pin_range(chip, - pinctrl_dev_get_devname(pctldev), -@@ -1089,7 +1138,11 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) - pinspec.args[1], - pinspec.args[2]); - if (ret) -+deb_verbose("**d"); -+//FIXIT - debug -+{ deb_verbose("**B"); - return ret; -+} - } else { - /* npins == 0: special range */ - if (pinspec.args[1]) { -@@ -1119,7 +1172,10 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) - ret = gpiochip_add_pingroup_range(chip, pctldev, - pinspec.args[0], name); - if (ret) -+//FIXIT - debug -+{ deb_verbose("C"); - return ret; -+} - } - } - -@@ -1133,7 +1189,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) { return 0; } - int of_gpiochip_add(struct gpio_chip *chip) - { - int ret; - -+ - if (!chip->of_node) - return 0; + if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS) + return -EINVAL; -@@ -1164,3 +1220,47 @@ void of_gpiochip_remove(struct gpio_chip *chip) +@@ -1164,3 +1184,47 @@ void of_gpiochip_remove(struct gpio_chip *chip) { of_node_put(chip->of_node); } @@ -1767,7 +1738,7 @@ index 647e77db82b1..2134815919de 100644 + of_node_get(chip->of_node); + +deb_verbose("7"); -+// ret = of_gpiochip_scan_gpios(chip); ++ret = of_gpiochip_scan_gpios(chip); + if (ret) + of_node_put(chip->of_node); + @@ -1811,22 +1782,16 @@ index 3ef71ca242ba..bf62aeee66a1 100644 /* If buf is not a number then try to find by name */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c -index 50abb1c20df0..7152dc772a61 100644 +index 50abb1c20df0..ed57ede9a7cb 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c -@@ -31,6 +31,29 @@ +@@ -31,6 +31,27 @@ #define CREATE_TRACE_POINTS #include -+ -+#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) -+#include "gpio-irq-proxy.h" // low level hooks for readl_x and writel_x -+ +#define GPIO_DEBUG +#define GPIO_DEBUG_VERBOSE + -+#endif // CONFIG_TEGRA_GPIO_GUEST_PROXY and CONFIG_TEGRA_GPIO_HOST_PROXY -+ +#ifdef GPIO_DEBUG + #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) + #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) @@ -1840,11 +1805,15 @@ index 50abb1c20df0..7152dc772a61 100644 +#else + #define deb_verbose(fmt, ...) +#endif ++ ++#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++#include "gpio-proxy.h" // low level hooks for readl_x and writel_x ++#endif // CONFIG_TEGRA_GPIO_GUEST_PROXY and CONFIG_TEGRA_GPIO_HOST_PROXY + /* Implementation infrastructure for GPIO interfaces. * * The GPIO programming interface allows for inlining speed-critical -@@ -45,11 +68,11 @@ +@@ -45,11 +66,11 @@ * * Otherwise, minimize overhead in what may be bitbanging codepaths. */ @@ -1858,7 +1827,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* Device and char device-related information */ static DEFINE_IDA(gpio_ida); -@@ -105,6 +128,8 @@ struct gpio_desc *gpio_to_desc(unsigned gpio) +@@ -105,6 +126,8 @@ struct gpio_desc *gpio_to_desc(unsigned gpio) { struct gpio_device *gdev; unsigned long flags; @@ -1867,7 +1836,7 @@ index 50abb1c20df0..7152dc772a61 100644 spin_lock_irqsave(&gpio_lock, flags); -@@ -140,6 +165,8 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, +@@ -140,6 +163,8 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, { struct gpio_device *gdev = gc->gpiodev; @@ -1876,7 +1845,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (hwnum >= gdev->ngpio) return ERR_PTR(-EINVAL); -@@ -170,6 +197,8 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); +@@ -170,6 +195,8 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); */ struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) { @@ -1885,7 +1854,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!desc || !desc->gdev) return NULL; return desc->gdev->chip; -@@ -183,6 +212,7 @@ static int gpiochip_find_base(int ngpio) +@@ -183,6 +210,7 @@ static int gpiochip_find_base(int ngpio) int base = ARCH_NR_GPIOS - ngpio; list_for_each_entry_reverse(gdev, &gpio_devices, list) { @@ -1893,7 +1862,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* found a free space? */ if (gdev->base + gdev->ngpio <= base) break; -@@ -214,6 +244,8 @@ int gpiod_get_direction(struct gpio_desc *desc) +@@ -214,6 +242,8 @@ int gpiod_get_direction(struct gpio_desc *desc) unsigned offset; int ret; @@ -1902,7 +1871,7 @@ index 50abb1c20df0..7152dc772a61 100644 gc = gpiod_to_chip(desc); offset = gpio_chip_hwgpio(desc); -@@ -253,9 +285,12 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) +@@ -253,9 +283,12 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) { struct gpio_device *prev, *next; @@ -1915,7 +1884,7 @@ index 50abb1c20df0..7152dc772a61 100644 return 0; } -@@ -263,6 +298,7 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) +@@ -263,6 +296,7 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) if (gdev->base + gdev->ngpio <= next->base) { /* add before first entry */ list_add(&gdev->list, &gpio_devices); @@ -1923,7 +1892,7 @@ index 50abb1c20df0..7152dc772a61 100644 return 0; } -@@ -270,18 +306,21 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) +@@ -270,18 +304,21 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) if (prev->base + prev->ngpio <= gdev->base) { /* add behind last entry */ list_add_tail(&gdev->list, &gpio_devices); @@ -1946,7 +1915,7 @@ index 50abb1c20df0..7152dc772a61 100644 return 0; } } -@@ -301,6 +340,8 @@ struct gpio_desc *gpio_name_to_desc(const char * const name) +@@ -301,6 +338,8 @@ struct gpio_desc *gpio_name_to_desc(const char * const name) struct gpio_device *gdev; unsigned long flags; @@ -1955,7 +1924,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!name) return NULL; -@@ -340,6 +381,8 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) +@@ -340,6 +379,8 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) struct gpio_device *gdev = gc->gpiodev; int i; @@ -1964,7 +1933,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* First check all names if they are unique */ for (i = 0; i != gc->ngpio; ++i) { struct gpio_desc *gpio; -@@ -375,6 +418,8 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip) +@@ -375,6 +416,8 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip) int ret, i; int count; @@ -1973,7 +1942,7 @@ index 50abb1c20df0..7152dc772a61 100644 count = fwnode_property_string_array_count(fwnode, "gpio-line-names"); if (count < 0) return 0; -@@ -409,6 +454,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) +@@ -409,6 +452,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) { unsigned long *p; @@ -1982,7 +1951,7 @@ index 50abb1c20df0..7152dc772a61 100644 p = bitmap_alloc(gc->ngpio, GFP_KERNEL); if (!p) return NULL; -@@ -421,6 +468,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) +@@ -421,6 +466,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) { @@ -1991,7 +1960,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask)) return 0; -@@ -433,6 +482,8 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) +@@ -433,6 +480,8 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) static int gpiochip_init_valid_mask(struct gpio_chip *gc) { @@ -2000,7 +1969,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (gc->init_valid_mask) return gc->init_valid_mask(gc, gc->valid_mask, -@@ -443,12 +494,16 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) +@@ -443,12 +492,16 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_free_valid_mask(struct gpio_chip *gc) { @@ -2017,7 +1986,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (gc->add_pin_ranges) return gc->add_pin_ranges(gc); -@@ -458,6 +513,8 @@ static int gpiochip_add_pin_ranges(struct gpio_chip *gc) +@@ -458,6 +511,8 @@ static int gpiochip_add_pin_ranges(struct gpio_chip *gc) bool gpiochip_line_is_valid(const struct gpio_chip *gc, unsigned int offset) { @@ -2026,7 +1995,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* No mask means all valid */ if (likely(!gc->valid_mask)) return true; -@@ -470,6 +527,8 @@ static void gpiodevice_release(struct device *dev) +@@ -470,6 +525,8 @@ static void gpiodevice_release(struct device *dev) struct gpio_device *gdev = dev_get_drvdata(dev); unsigned long flags; @@ -2035,7 +2004,7 @@ index 50abb1c20df0..7152dc772a61 100644 spin_lock_irqsave(&gpio_lock, flags); list_del(&gdev->list); spin_unlock_irqrestore(&gpio_lock, flags); -@@ -480,7 +539,7 @@ static void gpiodevice_release(struct device *dev) +@@ -480,7 +537,7 @@ static void gpiodevice_release(struct device *dev) kfree(gdev); } @@ -2044,7 +2013,7 @@ index 50abb1c20df0..7152dc772a61 100644 #define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt)) #define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev)) #else -@@ -490,13 +549,66 @@ static void gpiodevice_release(struct device *dev) +@@ -490,13 +547,66 @@ static void gpiodevice_release(struct device *dev) */ #define gcdev_register(gdev, devt) device_add(&(gdev)->dev) #define gcdev_unregister(gdev) device_del(&(gdev)->dev) @@ -2105,24 +2074,24 @@ index 50abb1c20df0..7152dc772a61 100644 + // we continue to populate gdev + +// FIXIT -- debug -+goto debug_end; ++//goto debug_end; + ret = gcdev_register(gdev, gpio_devt); + if (ret) return ret; -@@ -504,6 +616,9 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) +@@ -504,6 +614,9 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) if (ret) goto err_remove_device; +// FIXIT --debug -+debug_end: ++//debug_end: + /* From this point, the .release() function cleans up gpio_device */ gdev->dev.release = gpiodevice_release; pr_info("%s: registered GPIOs %d to %d on %s\n", -@@ -522,6 +637,8 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) +@@ -522,6 +635,8 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) struct gpio_desc *desc; int rv; @@ -2131,7 +2100,7 @@ index 50abb1c20df0..7152dc772a61 100644 desc = gpiochip_get_desc(gc, hog->chip_hwnum); if (IS_ERR(desc)) { chip_err(gc, "%s: unable to get GPIO desc: %ld\n", __func__, -@@ -542,6 +659,8 @@ static void machine_gpiochip_add(struct gpio_chip *gc) +@@ -542,6 +657,8 @@ static void machine_gpiochip_add(struct gpio_chip *gc) { struct gpiod_hog *hog; @@ -2140,7 +2109,7 @@ index 50abb1c20df0..7152dc772a61 100644 mutex_lock(&gpio_machine_hogs_mutex); list_for_each_entry(hog, &gpio_machine_hogs, list) { -@@ -557,6 +676,8 @@ static void gpiochip_setup_devs(void) +@@ -557,6 +674,8 @@ static void gpiochip_setup_devs(void) struct gpio_device *gdev; int ret; @@ -2149,7 +2118,7 @@ index 50abb1c20df0..7152dc772a61 100644 list_for_each_entry(gdev, &gpio_devices, list) { ret = gpiochip_setup_dev(gdev); if (ret) -@@ -576,6 +697,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -576,6 +695,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = gc->base; struct gpio_device *gdev; @@ -2158,7 +2127,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* * First: allocate and populate the internal stat container, and * set up the struct device. -@@ -591,13 +714,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -591,13 +712,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gdev->dev.of_node = gc->parent->of_node; } @@ -2174,7 +2143,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* * Assign fwnode depending on the result of the previous calls, -@@ -689,9 +812,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -689,9 +810,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier); @@ -2186,7 +2155,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (gc->names) ret = gpiochip_set_desc_names(gc); -@@ -793,6 +916,270 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -793,6 +914,269 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, } EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); @@ -2353,11 +2322,10 @@ index 50abb1c20df0..7152dc772a61 100644 + ret = of_gpiochip_add__redirect(gc); + if (ret) + goto err_free_gpiochip_mask; -+/* FIXIT -- debug -+ * skip this for debug ++ + if (ret) + goto err_free_gpiochip_mask; -+ */ ++ + ret = gpiochip_init_valid_mask(gc); + if (ret) + goto err_remove_of_chip; @@ -2457,7 +2425,7 @@ index 50abb1c20df0..7152dc772a61 100644 /** * gpiochip_get_data() - get per-subdriver data for the chip * @gc: GPIO chip -@@ -802,6 +1189,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); +@@ -802,6 +1186,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); */ void *gpiochip_get_data(struct gpio_chip *gc) { @@ -2466,7 +2434,7 @@ index 50abb1c20df0..7152dc772a61 100644 return gc->gpiodev->data; } EXPORT_SYMBOL_GPL(gpiochip_get_data); -@@ -818,6 +1207,8 @@ void gpiochip_remove(struct gpio_chip *gc) +@@ -818,6 +1204,8 @@ void gpiochip_remove(struct gpio_chip *gc) unsigned long flags; unsigned int i; @@ -2475,7 +2443,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ gpiochip_sysfs_unregister(gdev); gpiochip_free_hogs(gc); -@@ -875,6 +1266,8 @@ struct gpio_chip *gpiochip_find(void *data, +@@ -875,10 +1263,13 @@ struct gpio_chip *gpiochip_find(void *data, struct gpio_chip *gc = NULL; unsigned long flags; @@ -2484,7 +2452,12 @@ index 50abb1c20df0..7152dc772a61 100644 spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(gdev, &gpio_devices, list) if (gdev->chip && match(gdev->chip, data)) { -@@ -895,12 +1288,15 @@ static int gpiochip_match_name(struct gpio_chip *gc, void *data) + gc = gdev->chip; ++ deb_verbose("found: %p, chip: %s", gc, gc->label); + break; + } + +@@ -895,12 +1286,15 @@ static int gpiochip_match_name(struct gpio_chip *gc, void *data) return !strcmp(gc->label, name); } @@ -2502,7 +2475,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* * The following is irqchip helper code for gpiochips. -@@ -910,6 +1306,8 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc) +@@ -910,6 +1304,8 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc) { struct gpio_irq_chip *girq = &gc->irq; @@ -2511,7 +2484,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!girq->init_hw) return 0; -@@ -920,6 +1318,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -920,6 +1316,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) { struct gpio_irq_chip *girq = &gc->irq; @@ -2520,7 +2493,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!girq->init_valid_mask) return 0; -@@ -934,6 +1334,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -934,6 +1332,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) { @@ -2529,7 +2502,7 @@ index 50abb1c20df0..7152dc772a61 100644 bitmap_free(gc->irq.valid_mask); gc->irq.valid_mask = NULL; } -@@ -941,6 +1343,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) +@@ -941,6 +1341,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, unsigned int offset) { @@ -2538,7 +2511,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!gpiochip_line_is_valid(gc, offset)) return false; /* No mask means all valid */ -@@ -966,6 +1370,8 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, +@@ -966,6 +1368,8 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, struct gpio_irq_chip *girq = &gc->irq; struct device *dev = &gc->gpiodev->dev; @@ -2547,7 +2520,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!girq->domain) { chip_err(gc, "called %s before setting up irqchip\n", __func__); -@@ -1011,7 +1417,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gc, +@@ -1011,7 +1415,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gc, } EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); @@ -2556,7 +2529,7 @@ index 50abb1c20df0..7152dc772a61 100644 /** * gpiochip_set_hierarchical_irqchip() - connects a hierarchical irqchip -@@ -1250,6 +1656,8 @@ void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, +@@ -1250,6 +1654,8 @@ void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, { struct irq_fwspec *fwspec; @@ -2565,7 +2538,7 @@ index 50abb1c20df0..7152dc772a61 100644 fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); if (!fwspec) return NULL; -@@ -1269,6 +1677,8 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, +@@ -1269,6 +1675,8 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, { struct irq_fwspec *fwspec; @@ -2574,7 +2547,7 @@ index 50abb1c20df0..7152dc772a61 100644 fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); if (!fwspec) return NULL; -@@ -1296,7 +1706,7 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) +@@ -1296,7 +1704,7 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) return false; } @@ -2583,7 +2556,7 @@ index 50abb1c20df0..7152dc772a61 100644 /** * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip -@@ -1314,6 +1724,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, +@@ -1314,6 +1722,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, struct gpio_chip *gc = d->host_data; int ret = 0; @@ -2592,7 +2565,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!gpiochip_irqchip_irq_valid(gc, hwirq)) return -ENXIO; -@@ -1415,7 +1827,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) +@@ -1415,7 +1825,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) if (!gpiochip_irqchip_irq_valid(gc, offset)) return -ENXIO; @@ -2601,7 +2574,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (irq_domain_is_hierarchy(domain)) { struct irq_fwspec spec; -@@ -1426,7 +1838,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) +@@ -1426,7 +1836,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) return irq_create_fwspec_mapping(&spec); } @@ -2610,7 +2583,7 @@ index 50abb1c20df0..7152dc772a61 100644 return irq_create_mapping(domain, offset); } -@@ -1709,7 +2121,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, +@@ -1709,7 +2119,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, } gc->irq.threaded = threaded; of_node = gc->parent->of_node; @@ -2619,7 +2592,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* * If the gpiochip has an assigned OF node this takes precedence * FIXME: get rid of this and use gc->parent->of_node -@@ -1717,7 +2129,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, +@@ -1717,7 +2127,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, */ if (gc->of_node) of_node = gc->of_node; @@ -2628,7 +2601,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* * Specifying a default trigger is a terrible idea if DT or ACPI is * used to configure the interrupts, as you may end-up with -@@ -1796,7 +2208,7 @@ static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -1796,7 +2206,7 @@ static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) { } @@ -2637,7 +2610,7 @@ index 50abb1c20df0..7152dc772a61 100644 /** * gpiochip_generic_request() - request the gpio function for a pin -@@ -1805,10 +2217,12 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) +@@ -1805,10 +2215,12 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) */ int gpiochip_generic_request(struct gpio_chip *gc, unsigned offset) { @@ -2652,7 +2625,7 @@ index 50abb1c20df0..7152dc772a61 100644 return pinctrl_gpio_request(gc->gpiodev->base + offset); } -@@ -1821,10 +2235,12 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); +@@ -1821,10 +2233,12 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); */ void gpiochip_generic_free(struct gpio_chip *gc, unsigned offset) { @@ -2667,7 +2640,7 @@ index 50abb1c20df0..7152dc772a61 100644 pinctrl_gpio_free(gc->gpiodev->base + offset); } -@@ -1839,11 +2255,13 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free); +@@ -1839,11 +2253,13 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free); int gpiochip_generic_config(struct gpio_chip *gc, unsigned offset, unsigned long config) { @@ -2682,7 +2655,7 @@ index 50abb1c20df0..7152dc772a61 100644 /** * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping -@@ -1865,6 +2283,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, +@@ -1865,6 +2281,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, struct gpio_device *gdev = gc->gpiodev; int ret; @@ -2691,7 +2664,7 @@ index 50abb1c20df0..7152dc772a61 100644 pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); if (!pin_range) { chip_err(gc, "failed to allocate pin ranges\n"); -@@ -1923,6 +2343,8 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, +@@ -1923,6 +2341,8 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, struct gpio_device *gdev = gc->gpiodev; int ret; @@ -2700,7 +2673,7 @@ index 50abb1c20df0..7152dc772a61 100644 pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); if (!pin_range) { chip_err(gc, "failed to allocate pin ranges\n"); -@@ -1964,6 +2386,8 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) +@@ -1964,6 +2384,8 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) struct gpio_pin_range *pin_range, *tmp; struct gpio_device *gdev = gc->gpiodev; @@ -2709,7 +2682,7 @@ index 50abb1c20df0..7152dc772a61 100644 list_for_each_entry_safe(pin_range, tmp, &gdev->pin_ranges, node) { list_del(&pin_range->node); pinctrl_remove_gpio_range(pin_range->pctldev, -@@ -1973,7 +2397,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) +@@ -1973,7 +2395,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) } EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); @@ -2718,7 +2691,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* These "optional" allocation calls help prevent drivers from stomping * on each other, and help provide better diagnostics in debugfs. -@@ -1987,6 +2411,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) +@@ -1987,6 +2409,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) bool hogged = false; unsigned offset; @@ -2727,7 +2700,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (label) { /* Free desc->label if already allocated. */ if (desc->label) { -@@ -2092,6 +2518,8 @@ int gpiod_request(struct gpio_desc *desc, const char *label) +@@ -2092,6 +2516,8 @@ int gpiod_request(struct gpio_desc *desc, const char *label) int ret = -EPROBE_DEFER; struct gpio_device *gdev; @@ -2736,7 +2709,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); gdev = desc->gdev; -@@ -2108,6 +2536,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label) +@@ -2108,6 +2534,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label) return ret; } @@ -2744,7 +2717,7 @@ index 50abb1c20df0..7152dc772a61 100644 static bool gpiod_free_commit(struct gpio_desc *desc) { -@@ -2115,6 +2544,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2115,6 +2542,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) unsigned long flags; struct gpio_chip *gc; @@ -2753,7 +2726,7 @@ index 50abb1c20df0..7152dc772a61 100644 might_sleep(); gpiod_unexport(desc); -@@ -2141,12 +2572,12 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2141,12 +2570,12 @@ static bool gpiod_free_commit(struct gpio_desc *desc) clear_bit(FLAG_EDGE_RISING, &desc->flags); clear_bit(FLAG_EDGE_FALLING, &desc->flags); clear_bit(FLAG_IS_HOGGED, &desc->flags); @@ -2770,7 +2743,7 @@ index 50abb1c20df0..7152dc772a61 100644 ret = true; } -@@ -2159,6 +2590,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2159,6 +2588,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) void gpiod_free(struct gpio_desc *desc) { @@ -2779,7 +2752,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (desc && desc->gdev && gpiod_free_commit(desc)) { module_put(desc->gdev->owner); put_device(&desc->gdev->dev); -@@ -2166,6 +2599,7 @@ void gpiod_free(struct gpio_desc *desc) +@@ -2166,6 +2597,7 @@ void gpiod_free(struct gpio_desc *desc) WARN_ON(extra_checks); } } @@ -2787,7 +2760,7 @@ index 50abb1c20df0..7152dc772a61 100644 /** * gpiochip_is_requested - return string iff signal was requested -@@ -2184,6 +2618,8 @@ const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned offset) +@@ -2184,6 +2616,8 @@ const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned offset) { struct gpio_desc *desc; @@ -2796,7 +2769,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (offset >= gc->ngpio) return NULL; -@@ -2227,6 +2663,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, +@@ -2227,6 +2661,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, struct gpio_desc *desc = gpiochip_get_desc(gc, hwnum); int ret; @@ -2805,7 +2778,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (IS_ERR(desc)) { chip_err(gc, "failed to get GPIO descriptor\n"); return desc; -@@ -2274,6 +2712,8 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); +@@ -2274,6 +2710,8 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); static int gpio_do_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config) { @@ -2814,7 +2787,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!gc->set_config) return -ENOTSUPP; -@@ -2286,6 +2726,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) +@@ -2286,6 +2724,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) unsigned long config; unsigned arg; @@ -2823,7 +2796,7 @@ index 50abb1c20df0..7152dc772a61 100644 switch (mode) { case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_UP: -@@ -2305,6 +2747,8 @@ static int gpio_set_bias(struct gpio_desc *desc) +@@ -2305,6 +2745,8 @@ static int gpio_set_bias(struct gpio_desc *desc) int bias = 0; int ret = 0; @@ -2832,7 +2805,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) bias = PIN_CONFIG_BIAS_DISABLE; else if (test_bit(FLAG_PULL_UP, &desc->flags)) -@@ -2334,6 +2778,8 @@ int gpiod_direction_input(struct gpio_desc *desc) +@@ -2334,6 +2776,8 @@ int gpiod_direction_input(struct gpio_desc *desc) struct gpio_chip *gc; int ret = 0; @@ -2841,7 +2814,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); gc = desc->gdev->chip; -@@ -2381,6 +2827,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) +@@ -2381,6 +2825,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) int val = !!value; int ret = 0; @@ -2850,7 +2823,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* * It's OK not to specify .direction_output() if the gpiochip is * output-only, but if there is then not even a .set() operation it -@@ -2431,6 +2879,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) +@@ -2431,6 +2877,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) */ int gpiod_direction_output_raw(struct gpio_desc *desc, int value) { @@ -2859,7 +2832,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); return gpiod_direction_output_raw_commit(desc, value); } -@@ -2452,6 +2902,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) +@@ -2452,6 +2900,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) { int ret; @@ -2868,7 +2841,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; -@@ -2523,6 +2975,8 @@ int gpiod_timestamp_control(struct gpio_desc *desc, int enable) +@@ -2523,6 +2973,8 @@ int gpiod_timestamp_control(struct gpio_desc *desc, int enable) { struct gpio_chip *chip; @@ -2877,7 +2850,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); chip = desc->gdev->chip; if (!chip->timestamp_control) { -@@ -2550,6 +3004,8 @@ int gpiod_timestamp_read(struct gpio_desc *desc, u64 *ts) +@@ -2550,6 +3002,8 @@ int gpiod_timestamp_read(struct gpio_desc *desc, u64 *ts) u64 gpio_ts; int ret; @@ -2886,7 +2859,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); chip = desc->gdev->chip; if (!chip->timestamp_read) { -@@ -2578,6 +3034,8 @@ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) +@@ -2578,6 +3032,8 @@ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) { struct gpio_chip *gc; @@ -2895,7 +2868,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); gc = desc->gdev->chip; -@@ -2598,6 +3056,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) +@@ -2598,6 +3054,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) { unsigned long config; @@ -2904,7 +2877,7 @@ index 50abb1c20df0..7152dc772a61 100644 config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); return gpiod_set_config(desc, config); } -@@ -2618,6 +3078,8 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) +@@ -2618,6 +3076,8 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) int gpio; int rc; @@ -2913,7 +2886,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); /* * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for -@@ -2652,6 +3114,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_transitory); +@@ -2652,6 +3112,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_transitory); */ int gpiod_is_active_low(const struct gpio_desc *desc) { @@ -2922,7 +2895,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); return test_bit(FLAG_ACTIVE_LOW, &desc->flags); } -@@ -2663,6 +3127,8 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low); +@@ -2663,6 +3125,8 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low); */ void gpiod_toggle_active_low(struct gpio_desc *desc) { @@ -2931,7 +2904,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC_VOID(desc); change_bit(FLAG_ACTIVE_LOW, &desc->flags); } -@@ -2696,6 +3162,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) +@@ -2696,6 +3160,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) int offset; int value; @@ -2940,7 +2913,7 @@ index 50abb1c20df0..7152dc772a61 100644 gc = desc->gdev->chip; offset = gpio_chip_hwgpio(desc); value = gc->get ? gc->get(gc, offset) : -EIO; -@@ -2707,6 +3175,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) +@@ -2707,6 +3173,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) static int gpio_chip_get_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { @@ -2949,7 +2922,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (gc->get_multiple) { return gc->get_multiple(gc, mask, bits); } else if (gc->get) { -@@ -2731,6 +3201,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, +@@ -2731,6 +3199,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, { int ret, i = 0; @@ -2958,7 +2931,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* * Validate array_info against desc_array and its size. * It should immediately follow desc_array if both -@@ -2837,6 +3309,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, +@@ -2837,6 +3307,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, */ int gpiod_get_raw_value(const struct gpio_desc *desc) { @@ -2967,7 +2940,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); /* Should be using gpiod_get_raw_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -2858,6 +3332,8 @@ int gpiod_get_value(const struct gpio_desc *desc) +@@ -2858,6 +3330,8 @@ int gpiod_get_value(const struct gpio_desc *desc) { int value; @@ -2976,7 +2949,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); /* Should be using gpiod_get_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -2892,6 +3368,8 @@ int gpiod_get_raw_array_value(unsigned int array_size, +@@ -2892,6 +3366,8 @@ int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -2985,7 +2958,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(true, false, array_size, -@@ -2918,6 +3396,8 @@ int gpiod_get_array_value(unsigned int array_size, +@@ -2918,6 +3394,8 @@ int gpiod_get_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -2994,7 +2967,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(false, false, array_size, -@@ -2937,6 +3417,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) +@@ -2937,6 +3415,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) struct gpio_chip *gc = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); @@ -3003,7 +2976,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (value) { ret = gc->direction_input(gc, offset); } else { -@@ -2962,6 +3444,8 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value +@@ -2962,6 +3442,8 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value struct gpio_chip *gc = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); @@ -3012,7 +2985,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (value) { ret = gc->direction_output(gc, offset, 1); if (!ret) -@@ -2980,6 +3464,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) +@@ -2980,6 +3462,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) { struct gpio_chip *gc; @@ -3021,7 +2994,7 @@ index 50abb1c20df0..7152dc772a61 100644 gc = desc->gdev->chip; trace_gpio_value(desc_to_gpio(desc), 0, value); gc->set(gc, gpio_chip_hwgpio(desc), value); -@@ -2998,6 +3484,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) +@@ -2998,6 +3482,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) static void gpio_chip_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { @@ -3030,7 +3003,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (gc->set_multiple) { gc->set_multiple(gc, mask, bits); } else { -@@ -3017,6 +3505,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, +@@ -3017,6 +3503,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, { int i = 0; @@ -3039,7 +3012,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* * Validate array_info against desc_array and its size. * It should immediately follow desc_array if both -@@ -3122,6 +3612,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, +@@ -3122,6 +3610,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, */ void gpiod_set_raw_value(struct gpio_desc *desc, int value) { @@ -3048,7 +3021,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_raw_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -3140,6 +3632,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); +@@ -3140,6 +3630,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); */ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) { @@ -3057,7 +3030,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) -@@ -3163,6 +3657,8 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) +@@ -3163,6 +3655,8 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) */ void gpiod_set_value(struct gpio_desc *desc, int value) { @@ -3066,7 +3039,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -3188,6 +3684,8 @@ int gpiod_set_raw_array_value(unsigned int array_size, +@@ -3188,6 +3682,8 @@ int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3075,7 +3048,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(true, false, array_size, -@@ -3213,6 +3711,8 @@ int gpiod_set_array_value(unsigned int array_size, +@@ -3213,6 +3709,8 @@ int gpiod_set_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3084,7 +3057,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(false, false, array_size, -@@ -3228,6 +3728,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); +@@ -3228,6 +3726,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); */ int gpiod_cansleep(const struct gpio_desc *desc) { @@ -3093,7 +3066,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); return desc->gdev->chip->can_sleep; } -@@ -3240,6 +3742,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); +@@ -3240,6 +3740,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); */ int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name) { @@ -3102,7 +3075,7 @@ index 50abb1c20df0..7152dc772a61 100644 VALIDATE_DESC(desc); if (name) { name = kstrdup_const(name, GFP_KERNEL); -@@ -3266,6 +3770,8 @@ int gpiod_to_irq(const struct gpio_desc *desc) +@@ -3266,6 +3768,8 @@ int gpiod_to_irq(const struct gpio_desc *desc) struct gpio_chip *gc; int offset; @@ -3111,7 +3084,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* * Cannot VALIDATE_DESC() here as gpiod_to_irq() consumer semantics * requires this function to not return zero on an invalid descriptor -@@ -3300,6 +3806,8 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq); +@@ -3300,6 +3804,8 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq); int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc; @@ -3120,7 +3093,7 @@ index 50abb1c20df0..7152dc772a61 100644 desc = gpiochip_get_desc(gc, offset); if (IS_ERR(desc)) -@@ -3355,6 +3863,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3355,6 +3861,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc; @@ -3129,7 +3102,7 @@ index 50abb1c20df0..7152dc772a61 100644 desc = gpiochip_get_desc(gc, offset); if (IS_ERR(desc)) return; -@@ -3372,6 +3882,8 @@ void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3372,6 +3880,8 @@ void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc = gpiochip_get_desc(gc, offset); @@ -3138,7 +3111,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!IS_ERR(desc) && !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); -@@ -3382,6 +3894,8 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3382,6 +3892,8 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc = gpiochip_get_desc(gc, offset); @@ -3147,7 +3120,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!IS_ERR(desc) && !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) { /* -@@ -3397,6 +3911,8 @@ EXPORT_SYMBOL_GPL(gpiochip_enable_irq); +@@ -3397,6 +3909,8 @@ EXPORT_SYMBOL_GPL(gpiochip_enable_irq); bool gpiochip_line_is_irq(struct gpio_chip *gc, unsigned int offset) { @@ -3156,7 +3129,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (offset >= gc->ngpio) return false; -@@ -3430,6 +3946,8 @@ EXPORT_SYMBOL_GPL(gpiochip_relres_irq); +@@ -3430,6 +3944,8 @@ EXPORT_SYMBOL_GPL(gpiochip_relres_irq); bool gpiochip_line_is_open_drain(struct gpio_chip *gc, unsigned int offset) { @@ -3165,7 +3138,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (offset >= gc->ngpio) return false; -@@ -3439,6 +3957,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); +@@ -3439,6 +3955,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); bool gpiochip_line_is_open_source(struct gpio_chip *gc, unsigned int offset) { @@ -3174,7 +3147,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (offset >= gc->ngpio) return false; -@@ -3448,6 +3968,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); +@@ -3448,6 +3966,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); bool gpiochip_line_is_persistent(struct gpio_chip *gc, unsigned int offset) { @@ -3183,7 +3156,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (offset >= gc->ngpio) return false; -@@ -3466,6 +3988,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); +@@ -3466,6 +3986,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); */ int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) { @@ -3192,7 +3165,7 @@ index 50abb1c20df0..7152dc772a61 100644 might_sleep_if(extra_checks); VALIDATE_DESC(desc); return gpiod_get_raw_value_commit(desc); -@@ -3485,6 +4009,8 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) +@@ -3485,6 +4007,8 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) { int value; @@ -3201,7 +3174,7 @@ index 50abb1c20df0..7152dc772a61 100644 might_sleep_if(extra_checks); VALIDATE_DESC(desc); value = gpiod_get_raw_value_commit(desc); -@@ -3516,6 +4042,8 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, +@@ -3516,6 +4040,8 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3210,7 +3183,7 @@ index 50abb1c20df0..7152dc772a61 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3542,6 +4070,8 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, +@@ -3542,6 +4068,8 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3219,7 +3192,7 @@ index 50abb1c20df0..7152dc772a61 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3563,6 +4093,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); +@@ -3563,6 +4091,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); */ void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { @@ -3228,7 +3201,7 @@ index 50abb1c20df0..7152dc772a61 100644 might_sleep_if(extra_checks); VALIDATE_DESC_VOID(desc); gpiod_set_raw_value_commit(desc, value); -@@ -3581,6 +4113,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); +@@ -3581,6 +4111,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); */ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { @@ -3237,7 +3210,7 @@ index 50abb1c20df0..7152dc772a61 100644 might_sleep_if(extra_checks); VALIDATE_DESC_VOID(desc); gpiod_set_value_nocheck(desc, value); -@@ -3604,6 +4138,8 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, +@@ -3604,6 +4136,8 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3246,7 +3219,7 @@ index 50abb1c20df0..7152dc772a61 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3621,6 +4157,8 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) +@@ -3621,6 +4155,8 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) { unsigned int i; @@ -3255,7 +3228,7 @@ index 50abb1c20df0..7152dc772a61 100644 mutex_lock(&gpio_lookup_lock); for (i = 0; i < n; i++) -@@ -3646,6 +4184,8 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, +@@ -3646,6 +4182,8 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3264,7 +3237,7 @@ index 50abb1c20df0..7152dc772a61 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3661,6 +4201,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); +@@ -3661,6 +4199,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); */ void gpiod_add_lookup_table(struct gpiod_lookup_table *table) { @@ -3273,7 +3246,7 @@ index 50abb1c20df0..7152dc772a61 100644 mutex_lock(&gpio_lookup_lock); list_add_tail(&table->list, &gpio_lookup_list); -@@ -3675,6 +4217,8 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); +@@ -3675,6 +4215,8 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); */ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) { @@ -3282,7 +3255,7 @@ index 50abb1c20df0..7152dc772a61 100644 mutex_lock(&gpio_lookup_lock); list_del(&table->list); -@@ -3692,6 +4236,8 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) +@@ -3692,6 +4234,8 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) struct gpio_chip *gc; struct gpiod_hog *hog; @@ -3291,7 +3264,7 @@ index 50abb1c20df0..7152dc772a61 100644 mutex_lock(&gpio_machine_hogs_mutex); for (hog = &hogs[0]; hog->chip_label; hog++) { -@@ -3715,6 +4261,8 @@ static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) +@@ -3715,6 +4259,8 @@ static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) const char *dev_id = dev ? dev_name(dev) : NULL; struct gpiod_lookup_table *table; @@ -3300,7 +3273,7 @@ index 50abb1c20df0..7152dc772a61 100644 mutex_lock(&gpio_lookup_lock); list_for_each_entry(table, &gpio_lookup_list, list) { -@@ -3748,6 +4296,8 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, +@@ -3748,6 +4294,8 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, struct gpiod_lookup_table *table; struct gpiod_lookup *p; @@ -3309,7 +3282,7 @@ index 50abb1c20df0..7152dc772a61 100644 table = gpiod_find_lookup_table(dev); if (!table) return desc; -@@ -3813,6 +4363,8 @@ static int platform_gpio_count(struct device *dev, const char *con_id) +@@ -3813,6 +4361,8 @@ static int platform_gpio_count(struct device *dev, const char *con_id) struct gpiod_lookup *p; unsigned int count = 0; @@ -3318,7 +3291,7 @@ index 50abb1c20df0..7152dc772a61 100644 table = gpiod_find_lookup_table(dev); if (!table) return -ENOENT; -@@ -3858,6 +4410,8 @@ struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, +@@ -3858,6 +4408,8 @@ struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, char prop_name[32]; /* 32 is max size of property name */ unsigned int i; @@ -3327,7 +3300,7 @@ index 50abb1c20df0..7152dc772a61 100644 for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (con_id) snprintf(prop_name, sizeof(prop_name), "%s-%s", -@@ -3886,6 +4440,8 @@ int gpiod_count(struct device *dev, const char *con_id) +@@ -3886,6 +4438,8 @@ int gpiod_count(struct device *dev, const char *con_id) { int count = -ENOENT; @@ -3336,7 +3309,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) count = of_gpio_get_count(dev, con_id); else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) -@@ -3911,6 +4467,8 @@ EXPORT_SYMBOL_GPL(gpiod_count); +@@ -3911,6 +4465,8 @@ EXPORT_SYMBOL_GPL(gpiod_count); struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags) { @@ -3345,7 +3318,7 @@ index 50abb1c20df0..7152dc772a61 100644 return gpiod_get_index(dev, con_id, 0, flags); } EXPORT_SYMBOL_GPL(gpiod_get); -@@ -3951,6 +4509,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, +@@ -3951,6 +4507,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, { int ret; @@ -3354,7 +3327,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (lflags & GPIO_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); -@@ -4121,6 +4681,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, +@@ -4121,6 +4679,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, struct gpio_desc *desc = ERR_PTR(-ENODEV); int ret; @@ -3363,7 +3336,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (!fwnode) return ERR_PTR(-EINVAL); -@@ -4178,6 +4740,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, +@@ -4178,6 +4738,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, { struct gpio_desc *desc; @@ -3372,7 +3345,7 @@ index 50abb1c20df0..7152dc772a61 100644 desc = gpiod_get_index(dev, con_id, index, flags); if (IS_ERR(desc)) { if (PTR_ERR(desc) == -ENOENT) -@@ -4204,6 +4768,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, +@@ -4204,6 +4766,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, int hwnum; int ret; @@ -3381,7 +3354,7 @@ index 50abb1c20df0..7152dc772a61 100644 gc = gpiod_to_chip(desc); hwnum = gpio_chip_hwgpio(desc); -@@ -4235,6 +4801,8 @@ static void gpiochip_free_hogs(struct gpio_chip *gc) +@@ -4235,6 +4799,8 @@ static void gpiochip_free_hogs(struct gpio_chip *gc) { int id; @@ -3390,7 +3363,7 @@ index 50abb1c20df0..7152dc772a61 100644 for (id = 0; id < gc->ngpio; id++) { if (test_bit(FLAG_IS_HOGGED, &gc->gpiodev->descs[id].flags)) gpiochip_free_own_desc(&gc->gpiodev->descs[id]); -@@ -4263,6 +4831,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, +@@ -4263,6 +4829,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, struct gpio_chip *gc; int count, bitmap_size; @@ -3399,7 +3372,7 @@ index 50abb1c20df0..7152dc772a61 100644 count = gpiod_count(dev, con_id); if (count < 0) return ERR_PTR(count); -@@ -4383,6 +4953,8 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, +@@ -4383,6 +4951,8 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, { struct gpio_descs *descs; @@ -3408,7 +3381,7 @@ index 50abb1c20df0..7152dc772a61 100644 descs = gpiod_get_array(dev, con_id, flags); if (PTR_ERR(descs) == -ENOENT) return NULL; -@@ -4399,6 +4971,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); +@@ -4399,6 +4969,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); */ void gpiod_put(struct gpio_desc *desc) { @@ -3417,7 +3390,7 @@ index 50abb1c20df0..7152dc772a61 100644 if (desc) gpiod_free(desc); } -@@ -4412,6 +4986,8 @@ void gpiod_put_array(struct gpio_descs *descs) +@@ -4412,6 +4984,8 @@ void gpiod_put_array(struct gpio_descs *descs) { unsigned int i; @@ -3426,7 +3399,7 @@ index 50abb1c20df0..7152dc772a61 100644 for (i = 0; i < descs->ndescs; i++) gpiod_put(descs->desc[i]); -@@ -4423,6 +4999,8 @@ static int __init gpiolib_dev_init(void) +@@ -4423,6 +4997,8 @@ static int __init gpiolib_dev_init(void) { int ret; @@ -3435,7 +3408,7 @@ index 50abb1c20df0..7152dc772a61 100644 /* Register GPIO sysfs bus */ ret = bus_register(&gpio_bus_type); if (ret < 0) { -@@ -4442,13 +5020,13 @@ static int __init gpiolib_dev_init(void) +@@ -4442,13 +5018,13 @@ static int __init gpiolib_dev_init(void) #if IS_ENABLED(CONFIG_OF_DYNAMIC) && IS_ENABLED(CONFIG_OF_GPIO) WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier)); @@ -3451,7 +3424,7 @@ index 50abb1c20df0..7152dc772a61 100644 static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) { -@@ -4575,4 +5153,4 @@ static int __init gpiolib_debugfs_init(void) +@@ -4575,4 +5151,4 @@ static int __init gpiolib_debugfs_init(void) } subsys_initcall(gpiolib_debugfs_init); @@ -4404,7 +4377,7 @@ index c73b34e03aae..4c46d7faac5e 100644 pinctrl_init_debugfs(); return 0; diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c -index 3fb238714718..d48c607b9dab 100644 +index 3fb238714718..a7901c1d85af 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -106,6 +106,12 @@ struct pinctrl_dev *of_pinctrl_get(struct device_node *np) @@ -4438,7 +4411,7 @@ index 3fb238714718..d48c607b9dab 100644 +deb_debug("# B #"); ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps); +// DEBUG -+deb_debug("# C #"); ++deb_debug("# C -- OK #"); if (ret < 0) return ret; else if (num_maps == 0) { @@ -4447,7 +4420,7 @@ index 3fb238714718..d48c607b9dab 100644 dev_info(p->dev, "there is not valid maps for state %s\n", statename); +// DEBUG -+deb_debug("# D #"); ++deb_debug("# D -- error #"); return 0; } - @@ -4459,39 +4432,52 @@ index 3fb238714718..d48c607b9dab 100644 static int dt_remember_dummy_state(struct pinctrl *p, const char *statename) diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c -index f9ecbebe6442..f5541a687dc3 100644 +index f9ecbebe6442..776b1f20a469 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c -@@ -34,13 +34,28 @@ +@@ -34,14 +34,39 @@ #define EMMC_DPD_PARKING(x) (x << EMMC_PARKING_BIT) #define EMMC_PARKING_SET 0x1FFF -+// #define GPIO_DEBUG ++//#define GPIO_DEBUG ++//#define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG -+#define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) -+#define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) +#else -+#define deb_info(fmt, ...) -+#define deb_debug(fmt, ...) ++ #define deb_info(fmt, ...) ++ #define deb_debug(fmt, ...) +#endif + ++#ifdef GPIO_DEBUG_VERBOSE ++ #define deb_verbose deb_debug ++#else ++ #define deb_verbose(fmt, ...) ++#endif ++ ++#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) ++#include "../../gpio/gpio-proxy.h" // low level hooks for readl_x and writel_x ++#endif // CONFIG_TEGRA_GPIO_GUEST_PROXY and CONFIG_TEGRA_GPIO_HOST_PROXY + static inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg) { +- return readl(pmx->regs[bank] + reg); + deb_debug("\n"); + - return readl(pmx->regs[bank] + reg); ++ return readl_x(pmx->regs[bank] + reg); } static inline void pmx_writel(struct tegra_pmx *pmx, u32 val, u32 bank, u32 reg) { +- writel_relaxed(val, pmx->regs[bank] + reg); + deb_debug("\n"); + - writel_relaxed(val, pmx->regs[bank] + reg); ++ writel_relaxed_x(val, pmx->regs[bank] + reg); /* make sure pinmux register write completed */ pmx_readl(pmx, bank, reg); -@@ -50,6 +65,8 @@ static int tegra_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) + } +@@ -50,6 +75,8 @@ static int tegra_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); @@ -4500,7 +4486,7 @@ index f9ecbebe6442..f5541a687dc3 100644 return pmx->soc->ngroups; } -@@ -58,6 +75,8 @@ static const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev, +@@ -58,6 +85,8 @@ static const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev, { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); @@ -4509,7 +4495,7 @@ index f9ecbebe6442..f5541a687dc3 100644 return pmx->soc->groups[group].name; } -@@ -68,20 +87,24 @@ static int tegra_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, +@@ -68,20 +97,24 @@ static int tegra_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); @@ -4536,7 +4522,7 @@ index f9ecbebe6442..f5541a687dc3 100644 static const struct cfg_param { const char *property; -@@ -125,6 +148,8 @@ static int tegra_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, +@@ -125,6 +158,8 @@ static int tegra_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, struct property *prop; const char *group; @@ -4545,7 +4531,7 @@ index f9ecbebe6442..f5541a687dc3 100644 ret = of_property_read_string(np, "nvidia,function", &function); if (ret < 0) { /* EINVAL=missing, which is fine since it's optional */ -@@ -201,6 +226,8 @@ static int tegra_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, +@@ -201,6 +236,8 @@ static int tegra_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np; int ret; @@ -4554,7 +4540,7 @@ index f9ecbebe6442..f5541a687dc3 100644 reserved_maps = 0; *map = NULL; *num_maps = 0; -@@ -234,6 +261,8 @@ static int tegra_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) +@@ -234,6 +271,8 @@ static int tegra_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); @@ -4563,7 +4549,7 @@ index f9ecbebe6442..f5541a687dc3 100644 return pmx->soc->nfunctions; } -@@ -242,6 +271,8 @@ static const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev, +@@ -242,6 +281,8 @@ static const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev, { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); @@ -4572,7 +4558,7 @@ index f9ecbebe6442..f5541a687dc3 100644 return pmx->soc->functions[function].name; } -@@ -252,6 +283,8 @@ static int tegra_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, +@@ -252,6 +293,8 @@ static int tegra_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); @@ -4581,7 +4567,7 @@ index f9ecbebe6442..f5541a687dc3 100644 *groups = pmx->soc->functions[function].groups; *num_groups = pmx->soc->functions[function].ngroups; -@@ -267,6 +300,8 @@ static int tegra_pinctrl_set_mux(struct pinctrl_dev *pctldev, +@@ -267,6 +310,8 @@ static int tegra_pinctrl_set_mux(struct pinctrl_dev *pctldev, int i; u32 val; @@ -4590,7 +4576,7 @@ index f9ecbebe6442..f5541a687dc3 100644 g = &pmx->soc->groups[group]; if (WARN_ON(g->mux_reg < 0)) -@@ -300,6 +335,8 @@ static int tegra_pinctrl_gpio_save_config(struct pinctrl_dev *pctldev, +@@ -300,6 +345,8 @@ static int tegra_pinctrl_gpio_save_config(struct pinctrl_dev *pctldev, const unsigned *pins; int ret; @@ -4599,7 +4585,7 @@ index f9ecbebe6442..f5541a687dc3 100644 for (group = 0; group < pmx->soc->ngroups; ++group) { ret = tegra_pinctrl_get_group_pins(pctldev, group, &pins, &num_pins); if (ret < 0 || num_pins != 1) -@@ -330,6 +367,8 @@ static int tegra_pinctrl_gpio_restore_config(struct pinctrl_dev *pctldev, +@@ -330,6 +377,8 @@ static int tegra_pinctrl_gpio_restore_config(struct pinctrl_dev *pctldev, const unsigned *pins; int ret; @@ -4608,7 +4594,7 @@ index f9ecbebe6442..f5541a687dc3 100644 for (group = 0; group < pmx->soc->ngroups; ++group) { ret = tegra_pinctrl_get_group_pins(pctldev, group, &pins, &num_pins); if (ret < 0 || num_pins != 1) -@@ -358,6 +397,8 @@ static const struct tegra_pingroup *tegra_pinctrl_get_group(struct pinctrl_dev * +@@ -358,6 +407,8 @@ static const struct tegra_pingroup *tegra_pinctrl_get_group(struct pinctrl_dev * const unsigned int *pins; int ret; @@ -4617,7 +4603,7 @@ index f9ecbebe6442..f5541a687dc3 100644 for (group = 0; group < pmx->soc->ngroups; ++group) { ret = tegra_pinctrl_get_group_pins(pctldev, group, &pins, &num_pins); if (ret < 0) -@@ -384,6 +425,8 @@ static int tegra_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, +@@ -384,6 +435,8 @@ static int tegra_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, u32 value; int ret; @@ -4626,7 +4612,7 @@ index f9ecbebe6442..f5541a687dc3 100644 ret = tegra_pinctrl_gpio_save_config(pctldev, range, offset); if (ret) return ret; -@@ -416,6 +459,8 @@ static void tegra_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev, +@@ -416,6 +469,8 @@ static void tegra_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned int offset) { @@ -4635,7 +4621,7 @@ index f9ecbebe6442..f5541a687dc3 100644 tegra_pinctrl_gpio_restore_config(pctldev, range, offset); } -@@ -425,6 +470,8 @@ static int tegra_pinctrl_gpio_set_input(struct tegra_pmx *pmx, +@@ -425,6 +480,8 @@ static int tegra_pinctrl_gpio_set_input(struct tegra_pmx *pmx, { u32 value; @@ -4644,7 +4630,7 @@ index f9ecbebe6442..f5541a687dc3 100644 if (group->einput_bit < 0) return 0; -@@ -449,6 +496,8 @@ static int tegra_pinctrl_gpio_set_tristate(struct tegra_pmx *pmx, +@@ -449,6 +506,8 @@ static int tegra_pinctrl_gpio_set_tristate(struct tegra_pmx *pmx, { u32 value; @@ -4653,7 +4639,7 @@ index f9ecbebe6442..f5541a687dc3 100644 if (group->tri_bank < 0 || group->tri_reg < 0 || group->tri_bit < 0) return -EINVAL; -@@ -472,6 +521,8 @@ static int tegra_pinctrl_gpio_set_direction(struct pinctrl_dev *pctldev, +@@ -472,6 +531,8 @@ static int tegra_pinctrl_gpio_set_direction(struct pinctrl_dev *pctldev, const struct tegra_pingroup *group; int ret; @@ -4662,7 +4648,7 @@ index f9ecbebe6442..f5541a687dc3 100644 group = tegra_pinctrl_get_group(pctldev, offset); if (!group) return -EINVAL; -@@ -507,6 +558,8 @@ static int tegra_pinconf_reg(struct tegra_pmx *pmx, +@@ -507,6 +568,8 @@ static int tegra_pinconf_reg(struct tegra_pmx *pmx, bool report_err, s8 *bank, s32 *reg, s8 *bit, s8 *width) { @@ -4671,7 +4657,7 @@ index f9ecbebe6442..f5541a687dc3 100644 switch (param) { case TEGRA_PINCONF_PARAM_PULL: *bank = g->pupd_bank; -@@ -661,6 +714,7 @@ static int tegra_pinconf_reg(struct tegra_pmx *pmx, +@@ -661,6 +724,7 @@ static int tegra_pinconf_reg(struct tegra_pmx *pmx, static int tegra_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { @@ -4679,7 +4665,7 @@ index f9ecbebe6442..f5541a687dc3 100644 dev_err(pctldev->dev, "pin_config_get op not supported\n"); return -ENOTSUPP; } -@@ -669,6 +723,7 @@ static int tegra_pinconf_set(struct pinctrl_dev *pctldev, +@@ -669,6 +733,7 @@ static int tegra_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *configs, unsigned num_configs) { @@ -4687,7 +4673,7 @@ index f9ecbebe6442..f5541a687dc3 100644 dev_err(pctldev->dev, "pin_config_set op not supported\n"); return -ENOTSUPP; } -@@ -685,6 +740,8 @@ static int tegra_pinconf_group_get(struct pinctrl_dev *pctldev, +@@ -685,6 +750,8 @@ static int tegra_pinconf_group_get(struct pinctrl_dev *pctldev, s32 reg; u32 val, mask; @@ -4696,7 +4682,7 @@ index f9ecbebe6442..f5541a687dc3 100644 g = &pmx->soc->groups[group]; ret = tegra_pinconf_reg(pmx, g, param, true, &bank, ®, &bit, -@@ -718,6 +775,8 @@ static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, +@@ -718,6 +785,8 @@ static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, s32 reg; u32 val, mask; @@ -4705,7 +4691,7 @@ index f9ecbebe6442..f5541a687dc3 100644 g = &pmx->soc->groups[group]; for (i = 0; i < num_configs; i++) { -@@ -765,15 +824,17 @@ static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, +@@ -765,15 +834,17 @@ static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, return 0; } @@ -4724,7 +4710,7 @@ index f9ecbebe6442..f5541a687dc3 100644 if (!comma) return s; -@@ -791,6 +852,8 @@ static void tegra_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, +@@ -791,6 +862,8 @@ static void tegra_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, u32 val; u8 idx; @@ -4733,7 +4719,7 @@ index f9ecbebe6442..f5541a687dc3 100644 g = &pmx->soc->groups[group]; for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { -@@ -823,6 +886,8 @@ static void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, +@@ -823,6 +896,8 @@ static void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, const char *pname = "unknown"; int i; @@ -4742,7 +4728,7 @@ index f9ecbebe6442..f5541a687dc3 100644 for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { if (cfg_params[i].param == param) { pname = cfg_params[i].property; -@@ -832,7 +897,7 @@ static void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, +@@ -832,7 +907,7 @@ static void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, seq_printf(s, "%s=%d", strip_prefix(pname), arg); } @@ -4751,7 +4737,7 @@ index f9ecbebe6442..f5541a687dc3 100644 static const struct pinconf_ops tegra_pinconf_ops = { .pin_config_get = tegra_pinconf_get, -@@ -865,6 +930,8 @@ static void tegra_pinctrl_clear_parked_bits(struct tegra_pmx *pmx) +@@ -865,6 +940,8 @@ static void tegra_pinctrl_clear_parked_bits(struct tegra_pmx *pmx) const struct tegra_pingroup *g; u32 val; @@ -4760,7 +4746,7 @@ index f9ecbebe6442..f5541a687dc3 100644 for (i = 0; i < pmx->soc->ngroups; ++i) { g = &pmx->soc->groups[i]; if (g->parked_bitmask > 0) { -@@ -891,6 +958,8 @@ static size_t tegra_pinctrl_get_bank_size(struct device *dev, +@@ -891,6 +968,8 @@ static size_t tegra_pinctrl_get_bank_size(struct device *dev, struct platform_device *pdev = to_platform_device(dev); struct resource *res; @@ -4769,7 +4755,7 @@ index f9ecbebe6442..f5541a687dc3 100644 res = platform_get_resource(pdev, IORESOURCE_MEM, bank_id); return resource_size(res) / 4; -@@ -904,6 +973,8 @@ static int tegra_pinctrl_suspend(struct device *dev) +@@ -904,11 +983,13 @@ static int tegra_pinctrl_suspend(struct device *dev) size_t bank_size; unsigned int i, k; @@ -4778,7 +4764,13 @@ index f9ecbebe6442..f5541a687dc3 100644 for (i = 0; i < pmx->nbanks; i++) { bank_size = tegra_pinctrl_get_bank_size(dev, i); regs = pmx->regs[i]; -@@ -922,6 +993,8 @@ static int tegra_pinctrl_resume(struct device *dev) + for (k = 0; k < bank_size; k++) +- *backup_regs++ = readl_relaxed(regs++); ++ *backup_regs++ = readl_relaxed_x(regs++); + } + + return pinctrl_force_sleep(pmx->pctl); +@@ -922,15 +1003,17 @@ static int tegra_pinctrl_resume(struct device *dev) size_t bank_size; unsigned int i, k; @@ -4787,7 +4779,18 @@ index f9ecbebe6442..f5541a687dc3 100644 for (i = 0; i < pmx->nbanks; i++) { bank_size = tegra_pinctrl_get_bank_size(dev, i); regs = pmx->regs[i]; -@@ -961,6 +1034,8 @@ static bool tegra_pinctrl_gpio_node_has_range(struct tegra_pmx *pmx) + for (k = 0; k < bank_size; k++) +- writel_relaxed(*backup_regs++, regs++); ++ writel_relaxed_x(*backup_regs++, regs++); + } + + /* flush all the prior writes */ +- readl_relaxed(pmx->regs[0]); ++ readl_relaxed_x(pmx->regs[0]); + /* wait for pinctrl register read to complete */ + rmb(); + +@@ -961,6 +1044,8 @@ static bool tegra_pinctrl_gpio_node_has_range(struct tegra_pmx *pmx) struct device_node *np; bool has_prop = false; @@ -4796,7 +4799,7 @@ index f9ecbebe6442..f5541a687dc3 100644 if (of_property_read_bool(dev->of_node, "#gpio-range-cells")) return true; -@@ -985,6 +1060,8 @@ int tegra_pinctrl_probe(struct platform_device *pdev, +@@ -985,6 +1070,8 @@ int tegra_pinctrl_probe(struct platform_device *pdev, int fn, gn, gfn; unsigned long backup_regs_size = 0; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index f541ef2c8..dd499eee1 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -15,7 +15,7 @@ index dcecc9f6e33f..ed7d58d68ba6 100644 endmenu diff --git a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile 2024-05-02 12:06:58.097355696 +0000 -+++ b/drivers/Makefile 2024-05-14 10:00:52.054095256 +0000 ++++ b/drivers/Makefile 2024-05-22 13:27:59.382717623 +0000 @@ -193,0 +194,5 @@ +# +# @@ -48,10 +48,10 @@ index 0000000..2580e02 +obj-$(CONFIG_TEGRA_GPIO_GUEST_PROXY) += gpio-guest-proxy.o diff --git a/drivers/gpio-guest-proxy/gpio-guest-proxy.c b/drivers/gpio-guest-proxy/gpio-guest-proxy.c new file mode 100644 -index 0000000..bca5ff4 +index 0000000..b1a8cda --- /dev/null +++ b/drivers/gpio-guest-proxy/gpio-guest-proxy.c -@@ -0,0 +1,795 @@ +@@ -0,0 +1,796 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -70,7 +70,6 @@ index 0000000..bca5ff4 +#include +#include +#include -+#include "../gpio-host-proxy/gpio-host-proxy.h" +#include +#include +#include @@ -78,10 +77,7 @@ index 0000000..bca5ff4 +#include +#include + -+// we need a workaround for this -- relevant data copied to gpio-host-proxy.h -+// #include "$(abs_srctree)/drivers/gpio/gpiolib.h" -+ -+ ++#include "../gpio-host-proxy/gpio-host-proxy.h" + +#define DEVICE_NAME "gpio-guest" // Device name. +#define CLASS_NAME "char" @@ -166,12 +162,13 @@ index 0000000..bca5ff4 + kfree(io_buffer); +} + -+// redirect static inline u32 readl(const volatile void __iomem *addr) -+inline u32 readl_redirect( void * addr) { ++// redirect static inline u32 readl_x(const volatile void __iomem *addr) ++inline u32 readl_redirect( void * addr, const unsigned char rwltype) { + int ret = 0; + struct tegra_readl_writel msg; + + msg.signal = GPIO_READL; ++ msg.rwltype = rwltype; + msg.address = addr; + msg.value = 0; // value field is not used + @@ -180,11 +177,12 @@ index 0000000..bca5ff4 +} +EXPORT_SYMBOL_GPL(readl_redirect); + -+// redirect: static inline void writel(u32 value, volatile void __iomem *addr) -+inline void writel_redirect( u32 value, void * addr) { ++// redirect: static inline void writel_x(u32 value, volatile void __iomem *addr) ++inline void writel_redirect( u32 value, void * addr, const unsigned char rwltype) { + struct tegra_readl_writel msg; + + msg.signal = GPIO_WRITEL; ++ msg.rwltype = rwltype; + msg.address = addr; + msg.value = value; + @@ -613,7 +611,7 @@ index 0000000..bca5ff4 + + + // make gpio-host type call to gpio -+ deb_verbose("enter switch with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); ++ deb_verbose("Passthrough from guest with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); + + switch (kbuf->signal) { + case GPIO_REQ: @@ -626,9 +624,12 @@ index 0000000..bca5ff4 + } + chip = find_chip_by_id(kbuf->chipnum); + #ifdef GPIO_DEBUG_VERBOSE ++ // chip_alt = find_chip_by_id(kbuf->chipnum); + chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); -+ if(chip != chip_alt) ++ if(chip != chip_alt) { + deb_debug("conflicting chip pointers -- primary %p, alternative %p", chip, chip_alt); ++ chip = chip_alt; // we assume find_chip_by_name is more reliable ++ } + #endif + if(!chip) { + pr_err("In GPIO_REQ, chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); @@ -872,10 +873,10 @@ index 0000000..c2e0184 +obj-$(CONFIG_TEGRA_GPIO_HOST_PROXY) += gpio-host-proxy.o diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.c b/drivers/gpio-host-proxy/gpio-host-proxy.c new file mode 100644 -index 0000000..d61e894 +index 0000000..1df0ebb --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.c -@@ -0,0 +1,660 @@ +@@ -0,0 +1,686 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -892,13 +893,17 @@ index 0000000..d61e894 +//#include +#include +#include -+#include "../gpio-host-proxy/gpio-host-proxy.h" +#include +#include +#include +#include +#include + ++#include "../gpio-host-proxy/gpio-host-proxy.h" ++const unsigned char rwl_std_type = GPIO_RWL_STD; ++const unsigned char rwl_raw_type = GPIO_RWL_RAW; ++const unsigned char rwl_relaxed_type = GPIO_RWL_RELAXED; ++ +#define DEVICE_NAME "gpio-host" // Device name. +#define CLASS_NAME "chardrv" // < The device class -- this is a character device driver + @@ -1256,12 +1261,32 @@ index 0000000..d61e894 + switch (kbuf->signal) { + case GPIO_READL: + kbuf_rw = (struct tegra_readl_writel *)kbuf; -+ ret = (int)readl(kbuf_rw->address); ++ switch (kbuf_rw->rwltype) { ++ case GPIO_RWL_STD: ++ ret = (int)readl(kbuf_rw->address); ++ break; ++ case GPIO_RWL_RAW: ++ ret = (int)__raw_readl(kbuf_rw->address); ++ break; ++ case GPIO_RWL_RELAXED: ++ ret = (int)readl_relaxed(kbuf_rw->address); ++ break; ++ } + goto end; + break; + case GPIO_WRITEL: + kbuf_rw = (struct tegra_readl_writel *)kbuf; -+ writel(kbuf_rw->value, kbuf_rw->address); ++ switch (kbuf_rw->rwltype) { ++ case GPIO_RWL_STD: ++ writel(kbuf_rw->value, kbuf_rw->address); ++ break; ++ case GPIO_RWL_RAW: ++ __raw_writel(kbuf_rw->value, kbuf_rw->address); ++ break; ++ case GPIO_RWL_RELAXED: ++ writel_relaxed(kbuf_rw->value, kbuf_rw->address); ++ break; ++ } + goto retval; + break; + case GPIO_REQ: @@ -1274,16 +1299,18 @@ index 0000000..d61e894 + } + chip = find_chip_by_id(kbuf->chipnum); + #ifdef GPIO_DEBUG_VERBOSE ++ // chip_alt = find_chip_by_id(kbuf->chipnum); + chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); -+ if(chip != chip_alt) ++ if(chip != chip_alt) { + deb_debug("conflicting chip pointers -- primary %p, alternative %p", chip, chip_alt); ++ chip = chip_alt; // we assume find_chip_by_name is more reliable ++ } + #endif + if(!chip) { + pr_err("In GPIO_REQ, chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); + kfree(kbuf); + return -ENODEV; + } -+ + deb_verbose("GPIO_REQ, using GPIO chip %s, for device %d, pvalue = %p", chip->label, kbuf->chipnum, chip); + ret = chip->request(chip, kbuf->offset); + goto end; @@ -1538,10 +1565,10 @@ index 0000000..d61e894 +module_exit(gpio_host_proxy_exit); diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.h b/drivers/gpio-host-proxy/gpio-host-proxy.h new file mode 100644 -index 0000000..2996151 +index 0000000..88ea920 --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.h -@@ -0,0 +1,93 @@ +@@ -0,0 +1,102 @@ +#ifndef __GPIO_HOST_PROXY__H__ +#define __GPIO_HOST_PROXY__H__ + @@ -1594,11 +1621,20 @@ index 0000000..2996151 + +#define GPIO_READL 'R' +#define GPIO_WRITEL 'W' ++#define GPIO_RWL_STD '0' // general readl/writeL ++#define GPIO_RWL_RAW '1' // __raw assembler version of readl/writel ++#define GPIO_RWL_RELAXED '2' // __relaxed assembler version of readl/writel ++ ++// Note this extern is also in gpio-proxy.h in the kernel source tree (this proxy code might be in an overlay) ++extern const unsigned char rwl_std_type; ++extern const unsigned char rwl_raw_type; ++extern const unsigned char rwl_relaxed_type; + +// sizeof is rounded to even 64 bit passhtough writes -- no need to optimise size further on an aarch64 +struct tegra_readl_writel { + unsigned char signal; // Note: signal field is overloaded (based on field offset) with signal in struct tegra_gpio_pt -+ unsigned char pad[3]; // to get an even 64 bit message size ++ unsigned char rwltype; // type of readl/writel call ++ unsigned char pad[2]; // to get an even mod 64 bit message size + u32 value; + void * address; +}; diff --git a/modules/microvm/virtualization/microvm/netvm.nix b/modules/microvm/virtualization/microvm/netvm.nix index bb47d575c..680bf2831 100644 --- a/modules/microvm/virtualization/microvm/netvm.nix +++ b/modules/microvm/virtualization/microvm/netvm.nix @@ -118,7 +118,8 @@ in { config = lib.mkIf cfg.enable { microvm.vms."${vmName}" = { - autostart = true; + autostart = false; + # autostart = true; config = netvmBaseConfiguration // { diff --git a/targets/nvidia-jetson-orin/flake-module.nix b/targets/nvidia-jetson-orin/flake-module.nix index b95d0dd18..a28022de7 100644 --- a/targets/nvidia-jetson-orin/flake-module.nix +++ b/targets/nvidia-jetson-orin/flake-module.nix @@ -59,10 +59,8 @@ hardware.nvidia.orin = { enable = true; somType = som; - # agx.enableNetvmWlanPCIPassthrough = som == "agx"; - agx.enableNetvmWlanPCIPassthrough = false; - # nx.enableNetvmEthernetPCIPassthrough = som == "nx"; - nx.enableNetvmEthernetPCIPassthrough = false; + agx.enableNetvmWlanPCIPassthrough = som == "agx"; + nx.enableNetvmEthernetPCIPassthrough = som == "nx"; agx.enableGPIOPassthrough = som == "agx"; }; From 6b4dc82f57e5382fd2a79bb863309bd97c559c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Wed, 26 Jun 2024 11:57:23 +0000 Subject: [PATCH 20/26] generation 207 -- drivers/kernel: 91eacb4cc1b6eee65cbef01e028442ba16699608/72089e71a1f3d9f2d854711c6e16b703f1a426da --- .../patches/0003-gpio-virt-kernel.patch | 400 ++++++++++-------- .../patches/0004-gpio-virt-drivers.patch | 208 +++++---- 2 files changed, 353 insertions(+), 255 deletions(-) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch index 00a4c6e93..f4c17a9b9 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch @@ -346,7 +346,7 @@ index f66fc17faee4..01a24add5f6f 100644 } subsys_initcall(tegra_gpio_init); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c -index 5e57824b283e..f7835f80cd66 100644 +index 5e57824b283e..685ef7b540da 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -20,6 +20,46 @@ @@ -769,7 +769,7 @@ index 5e57824b283e..f7835f80cd66 100644 base = tegra186_gpio_get_base(gpio, offset); if (base == NULL) return -ENXIO; -@@ -708,11 +802,11 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, +@@ -708,15 +802,18 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, debounce = DIV_ROUND_UP(debounce, USEC_PER_MSEC); value = TEGRA186_GPIO_DEBOUNCE_CONTROL_THRESHOLD(debounce); @@ -784,7 +784,14 @@ index 5e57824b283e..f7835f80cd66 100644 return 0; } -@@ -725,6 +819,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) + ++// TODO DEBUG FIXIT ++// for guest: ++// this function cannot maybe be redirected because we need to set up pointer 'chip' in guest! + static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) + { + struct tegra_gpio *gpio = gpiochip_get_data(chip); +@@ -725,6 +822,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) unsigned int i, j; int err; @@ -793,7 +800,7 @@ index 5e57824b283e..f7835f80cd66 100644 if (!gpio->soc->pinmux || gpio->soc->num_pin_ranges == 0) return 0; -@@ -768,6 +864,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, +@@ -768,6 +867,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, struct tegra_gpio *gpio = gpiochip_get_data(chip); unsigned int port, pin, i, offset = 0; @@ -802,7 +809,7 @@ index 5e57824b283e..f7835f80cd66 100644 if (WARN_ON(chip->of_gpio_n_cells < 2)) return -EINVAL; -@@ -803,7 +901,7 @@ static void tegra186_irq_ack(struct irq_data *data) +@@ -803,7 +904,7 @@ static void tegra186_irq_ack(struct irq_data *data) if (WARN_ON(base == NULL)) return; @@ -811,7 +818,7 @@ index 5e57824b283e..f7835f80cd66 100644 } static void tegra186_irq_mask(struct irq_data *data) -@@ -817,9 +915,9 @@ static void tegra186_irq_mask(struct irq_data *data) +@@ -817,9 +918,9 @@ static void tegra186_irq_mask(struct irq_data *data) if (WARN_ON(base == NULL)) return; @@ -823,7 +830,7 @@ index 5e57824b283e..f7835f80cd66 100644 } static void tegra186_irq_unmask(struct irq_data *data) -@@ -833,9 +931,9 @@ static void tegra186_irq_unmask(struct irq_data *data) +@@ -833,9 +934,9 @@ static void tegra186_irq_unmask(struct irq_data *data) if (WARN_ON(base == NULL)) return; @@ -835,7 +842,7 @@ index 5e57824b283e..f7835f80cd66 100644 } static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) -@@ -849,7 +947,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) +@@ -849,7 +950,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) if (WARN_ON(base == NULL)) return -ENODEV; @@ -844,7 +851,7 @@ index 5e57824b283e..f7835f80cd66 100644 value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK; value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL; -@@ -883,7 +981,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) +@@ -883,7 +984,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) return -EINVAL; } @@ -853,7 +860,7 @@ index 5e57824b283e..f7835f80cd66 100644 if ((type & IRQ_TYPE_EDGE_BOTH) == 0) irq_set_handler_locked(data, handle_level_irq); -@@ -930,7 +1028,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) +@@ -930,7 +1031,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) if (j == gpio->num_irqs_per_bank) goto skip; @@ -862,7 +869,7 @@ index 5e57824b283e..f7835f80cd66 100644 for_each_set_bit(pin, &value, port->pins) { irq = irq_find_mapping(domain, offset + pin); -@@ -1037,6 +1135,8 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1037,6 +1138,8 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) unsigned int i, j; u32 value; @@ -871,7 +878,7 @@ index 5e57824b283e..f7835f80cd66 100644 for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; unsigned int offset, p = port->port; -@@ -1044,7 +1144,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1044,7 +1147,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) base = gpio->secure + port->bank * 0x1000 + 0x800; @@ -880,7 +887,7 @@ index 5e57824b283e..f7835f80cd66 100644 /* * For controllers that haven't been locked down yet, make -@@ -1058,7 +1158,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1058,7 +1161,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) */ for (j = 0; j < gpio->num_irqs_per_bank; j++) { dev_dbg(dev, "programming default interrupt routing for port %s\n", @@ -889,7 +896,7 @@ index 5e57824b283e..f7835f80cd66 100644 offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j); -@@ -1073,9 +1173,9 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1073,9 +1176,9 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) */ if (j == 0) { @@ -901,7 +908,7 @@ index 5e57824b283e..f7835f80cd66 100644 } } } -@@ -1107,6 +1207,182 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +@@ -1107,6 +1210,184 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) return -EINVAL; } @@ -961,7 +968,9 @@ index 5e57824b283e..f7835f80cd66 100644 + gpio->gpio.timestamp_control = tegra_gpio_timestamp_control_redirect; + gpio->gpio.timestamp_read = tegra_gpio_timestamp_read_redirect; + gpio->gpio.suspend_configure = tegra_gpio_suspend_configure_redirect; -+ gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges_redirect; ++ // DEBUG ++ // gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges_redirect; ++ gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; + gpio->gpio.base = -1; + } +#endif @@ -1084,7 +1093,7 @@ index 5e57824b283e..f7835f80cd66 100644 static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; -@@ -1120,17 +1396,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1120,17 +1401,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) int value; void __iomem *base; @@ -1129,7 +1138,7 @@ index 5e57824b283e..f7835f80cd66 100644 /* count the number of banks in the controller */ for (i = 0; i < gpio->soc->num_ports; i++) -@@ -1139,11 +1438,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1139,11 +1443,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->num_banks++; @@ -1146,7 +1155,7 @@ index 5e57824b283e..f7835f80cd66 100644 sizeof(*gpio->gpio_rval), GFP_KERNEL); if (!gpio->gpio_rval) return -ENOMEM; -@@ -1154,9 +1457,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1154,9 +1462,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -EINVAL; } @@ -1159,7 +1168,7 @@ index 5e57824b283e..f7835f80cd66 100644 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gte"); if (!res) { dev_err(&pdev->dev, "Missing gte MEM resource\n"); -@@ -1172,14 +1478,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1172,14 +1483,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) } err = platform_irq_count(pdev); @@ -1183,7 +1192,7 @@ index 5e57824b283e..f7835f80cd66 100644 gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), GFP_KERNEL); -@@ -1188,27 +1500,43 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1188,27 +1505,43 @@ static int tegra186_gpio_probe(struct platform_device *pdev) for (i = 0; i < gpio->num_irq; i++) { err = platform_get_irq(pdev, i); @@ -1243,7 +1252,7 @@ index 5e57824b283e..f7835f80cd66 100644 for (i = 0; i < gpio->soc->num_ports; i++) gpio->gpio.ngpio += gpio->soc->ports[i].pins; -@@ -1229,6 +1557,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1229,6 +1562,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -ENOMEM; names[offset + j] = name; @@ -1251,7 +1260,7 @@ index 5e57824b283e..f7835f80cd66 100644 } offset += port->pins; -@@ -1260,6 +1589,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1260,6 +1594,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parent_handler_data = gpio; irq->num_parents = gpio->num_irq; @@ -1260,13 +1269,13 @@ index 5e57824b283e..f7835f80cd66 100644 /* * To simplify things, use a single interrupt per bank for now. Some -@@ -1283,8 +1614,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1283,8 +1619,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parents = gpio->irq; } + #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) + // we cannot physically set up irq on guest -+ if(kernel_is_on_guest) { ++ if(!kernel_is_on_guest) { + #endif if (gpio->soc->num_irqs_per_bank > 1) tegra186_gpio_init_route_mapping(gpio); @@ -1276,7 +1285,7 @@ index 5e57824b283e..f7835f80cd66 100644 np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (!of_device_is_available(np)) -@@ -1315,9 +1653,24 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1315,9 +1658,36 @@ static int tegra186_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, gpio); @@ -1284,7 +1293,16 @@ index 5e57824b283e..f7835f80cd66 100644 - if (err < 0) - return err; + #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) ++ /* of these functions: ++ * int devm_gpiochip_add_data__redirect(struct device *dev, struct gpio_chip *gc, void *data) ++ * int of_gpiochip_add__redirect(struct gpio_chip *chip) ++ * static int gpiochip_setup_dev__redirect(struct gpio_device *gdev) ++ * int gpiochip_add_data__redirect(struct gpio_chip *gc, void *data) ++ * only devm_gpiochip_add_data needs to be redirected and passthrough ++ * (assuming passthrough is the solution) ++ */ + if(kernel_is_on_guest) { ++ // passthrough function if we are guest + err = devm_gpiochip_add_data__redirect(&pdev->dev, &gpio->gpio, gpio); + } + else @@ -1298,13 +1316,16 @@ index 5e57824b283e..f7835f80cd66 100644 + return err; + } + -+ #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) -+ if(kernel_is_on_guest) goto guest_skip; -+ #endif ++ // DEBUG ++ // #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) ++ // if(kernel_is_on_guest) goto guest_skip; ++ // #endif ++ ++ /* on guest, we could possibly passsthrough the whole loop below for better performance */ if (gpio->soc->is_hw_ts_sup) { for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) { -@@ -1329,24 +1682,25 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1329,24 +1699,25 @@ static int tegra186_gpio_probe(struct platform_device *pdev) if (WARN_ON(base == NULL)) return -EINVAL; @@ -1327,7 +1348,7 @@ index 5e57824b283e..f7835f80cd66 100644 - return 0; + #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) + preserve_tegrachip(gpio); -+ guest_skip: ++ // guest_skip: + #endif + return 0; } @@ -1337,7 +1358,7 @@ index 5e57824b283e..f7835f80cd66 100644 static int tegra_gpio_resume_early(struct device *dev) { struct tegra_gpio *gpio = dev_get_drvdata(dev); -@@ -1366,9 +1720,9 @@ static int tegra_gpio_resume_early(struct device *dev) +@@ -1366,9 +1737,9 @@ static int tegra_gpio_resume_early(struct device *dev) regs->restore_needed = false; @@ -1350,7 +1371,7 @@ index 5e57824b283e..f7835f80cd66 100644 } return 0; -@@ -1723,7 +2077,9 @@ static struct platform_driver tegra186_gpio_driver = { +@@ -1723,7 +2094,9 @@ static struct platform_driver tegra186_gpio_driver = { .probe = tegra186_gpio_probe, .remove = tegra186_gpio_remove, }; @@ -1561,10 +1582,20 @@ index 2613881a66e6..1b052acf3753 100644 cdev_device_del(&gdev->chrdev, &gdev->dev); } diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c -index 7dbce4c4ebdf..a8ec8a115c61 100644 +index 7dbce4c4ebdf..117f0e70069b 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c -@@ -503,6 +503,7 @@ static void devm_gpio_chip_release(struct device *dev, void *res) +@@ -377,9 +377,6 @@ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs) + } + EXPORT_SYMBOL_GPL(devm_gpiod_put_array); + +- +- +- + static void devm_gpio_release(struct device *dev, void *res) + { + unsigned *gpio = res; +@@ -503,6 +500,7 @@ static void devm_gpio_chip_release(struct device *dev, void *res) * gc->base is invalid or already associated with a different chip. * Otherwise it returns zero as a success code. */ @@ -1572,7 +1603,7 @@ index 7dbce4c4ebdf..a8ec8a115c61 100644 int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, void *data, struct lock_class_key *lock_key, struct lock_class_key *request_key) -@@ -527,3 +528,37 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, vo +@@ -527,3 +525,38 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, vo return 0; } EXPORT_SYMBOL_GPL(devm_gpiochip_add_data_with_key); @@ -1606,6 +1637,7 @@ index 7dbce4c4ebdf..a8ec8a115c61 100644 + *ptr = gc; + devres_add(dev, ptr); + ++ + return 0; +} +EXPORT_SYMBOL_GPL(devm_gpiochip_add_data__redirect); @@ -1664,7 +1696,7 @@ index 30e2476a6dc4..aab40f1cd52f 100644 err = gpio_request_one(array->gpio, array->flags, array->label); if (err) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c -index 647e77db82b1..994b57b11c0e 100644 +index 647e77db82b1..02f779c4cd65 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -24,6 +24,27 @@ @@ -1695,7 +1727,29 @@ index 647e77db82b1..994b57b11c0e 100644 /** * of_gpio_spi_cs_get_count() - special GPIO counting for SPI * @dev: Consuming device -@@ -1141,7 +1162,6 @@ int of_gpiochip_add(struct gpio_chip *chip) +@@ -1061,16 +1082,21 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) + group_names = of_find_property(np, group_names_propname, NULL); + + for (;; index++) { ++ deb_verbose("index at A: %d", index); + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, + index, &pinspec); + if (ret) + break; + ++ deb_verbose("index at B: %d", index); ++ + pctldev = of_pinctrl_get(pinspec.np); + of_node_put(pinspec.np); + if (!pctldev) + return -EPROBE_DEFER; + ++ deb_verbose("index at C: %d", index); ++ + if (pinspec.args[2]) { + if (group_names) { + of_property_read_string_index(np, +@@ -1141,7 +1167,6 @@ int of_gpiochip_add(struct gpio_chip *chip) chip->of_gpio_n_cells = 2; chip->of_xlate = of_gpio_simple_xlate; } @@ -1703,7 +1757,7 @@ index 647e77db82b1..994b57b11c0e 100644 if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS) return -EINVAL; -@@ -1164,3 +1184,47 @@ void of_gpiochip_remove(struct gpio_chip *chip) +@@ -1164,3 +1189,51 @@ void of_gpiochip_remove(struct gpio_chip *chip) { of_node_put(chip->of_node); } @@ -1731,6 +1785,10 @@ index 647e77db82b1..994b57b11c0e 100644 + +deb_verbose("5"); + ret = of_gpiochip_add_pin_range(chip); ++ ++// TODO guest fails here: returns -517 ++ ++deb_verbose("of_gpiochip_add_pin_range returns: %d", ret); + if (ret) + return ret; + @@ -1782,7 +1840,7 @@ index 3ef71ca242ba..bf62aeee66a1 100644 /* If buf is not a number then try to find by name */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c -index 50abb1c20df0..ed57ede9a7cb 100644 +index 50abb1c20df0..fb9f7b6cfaed 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -31,6 +31,27 @@ @@ -1880,7 +1938,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (list_empty(&gpio_devices)) { /* initial entry in list */ list_add_tail(&gdev->list, &gpio_devices); -+deb_info("debug 1"); ++deb_debug("debug 1"); return 0; } @@ -1888,7 +1946,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (gdev->base + gdev->ngpio <= next->base) { /* add before first entry */ list_add(&gdev->list, &gpio_devices); -+deb_info("debug 2"); ++deb_debug("debug 2"); return 0; } @@ -1896,14 +1954,14 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (prev->base + prev->ngpio <= gdev->base) { /* add behind last entry */ list_add_tail(&gdev->list, &gpio_devices); -+deb_info("debug 3"); ++deb_debug("debug 3"); return 0; } list_for_each_entry_safe(prev, next, &gpio_devices, list) { /* at the end of the list */ if (&next->list == &gpio_devices) -+{ deb_info("debug 4, prev= 0x%p, next = 0x%p", prev, next); ++{ deb_debug("debug 4, prev= 0x%p, next = 0x%p", prev, next); break; - +} @@ -1911,7 +1969,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (prev->base + prev->ngpio <= gdev->base && gdev->base + gdev->ngpio <= next->base) { list_add(&gdev->list, &prev->list); -+deb_info("debug 5"); ++deb_debug("debug 5"); return 0; } } @@ -2155,7 +2213,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (gc->names) ret = gpiochip_set_desc_names(gc); -@@ -793,6 +914,269 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -793,6 +914,277 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, } EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); @@ -2319,16 +2377,21 @@ index 50abb1c20df0..ed57ede9a7cb 100644 + if (ret) + goto err_remove_from_list; + -+ ret = of_gpiochip_add__redirect(gc); -+ if (ret) -+ goto err_free_gpiochip_mask; + ++ /* TODO guest driver fails on of_gpiochip_add */ ++ ret = of_gpiochip_add__redirect(gc); + if (ret) ++// DEBUG ++{ deb_verbose("of_gpiochip_add__redirect fails, with: %d", ret); + goto err_free_gpiochip_mask; ++} + + ret = gpiochip_init_valid_mask(gc); + if (ret) ++// DEBUG ++{ deb_verbose("gpiochip_init_valid_mask fails, with: %d", ret); + goto err_remove_of_chip; ++} + + for (i = 0; i < gc->ngpio; i++) { + struct gpio_desc *desc = &gdev->descs[i]; @@ -2344,7 +2407,10 @@ index 50abb1c20df0..ed57ede9a7cb 100644 + + ret = gpiochip_add_pin_ranges(gc); + if (ret) ++// DEBUG ++{ deb_verbose("gpiochip_add_pin_ranges, with: %d", ret); + goto err_remove_of_chip; ++} + + acpi_gpiochip_add(gc); + @@ -2378,41 +2444,41 @@ index 50abb1c20df0..ed57ede9a7cb 100644 + return 0; + +err_remove_irqchip: -+deb_info("A"); ++deb_debug("A"); + gpiochip_irqchip_remove(gc); +err_remove_irqchip_mask: -+deb_info("B"); ++deb_debug("B"); + gpiochip_irqchip_free_valid_mask(gc); +err_remove_acpi_chip: -+deb_info("C"); ++deb_debug("C"); + acpi_gpiochip_remove(gc); +err_remove_of_chip: -+deb_info("D"); ++deb_debug("D"); + gpiochip_free_hogs(gc); + of_gpiochip_remove(gc); +err_free_gpiochip_mask: -+deb_info("E -- of_gpiochip_add__redirect fails"); ++deb_debug("E"); + gpiochip_remove_pin_ranges(gc); + gpiochip_free_valid_mask(gc); +err_remove_from_list: -+deb_info("F"); ++deb_debug("F"); + spin_lock_irqsave(&gpio_lock, flags); + list_del(&gdev->list); + spin_unlock_irqrestore(&gpio_lock, flags); +err_free_label: -+deb_info("G"); ++deb_debug("G"); + kfree_const(gdev->label); +err_free_descs: -+deb_info("H"); ++deb_debug("H"); + kfree(gdev->descs); +err_free_dev_name: -+deb_info("I"); ++deb_debug("I"); + kfree(dev_name(&gdev->dev)); +err_free_ida: -+deb_info("J"); ++deb_debug("J"); + ida_free(&gpio_ida, gdev->id); +err_free_gdev: -+deb_info("K"); ++deb_debug("K"); + /* failures here can mean systems won't boot... */ + pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__, + gdev->base, gdev->base + gdev->ngpio - 1, @@ -2425,7 +2491,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /** * gpiochip_get_data() - get per-subdriver data for the chip * @gc: GPIO chip -@@ -802,6 +1186,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); +@@ -802,6 +1194,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); */ void *gpiochip_get_data(struct gpio_chip *gc) { @@ -2434,7 +2500,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 return gc->gpiodev->data; } EXPORT_SYMBOL_GPL(gpiochip_get_data); -@@ -818,6 +1204,8 @@ void gpiochip_remove(struct gpio_chip *gc) +@@ -818,6 +1212,8 @@ void gpiochip_remove(struct gpio_chip *gc) unsigned long flags; unsigned int i; @@ -2443,7 +2509,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ gpiochip_sysfs_unregister(gdev); gpiochip_free_hogs(gc); -@@ -875,10 +1263,13 @@ struct gpio_chip *gpiochip_find(void *data, +@@ -875,10 +1271,13 @@ struct gpio_chip *gpiochip_find(void *data, struct gpio_chip *gc = NULL; unsigned long flags; @@ -2457,7 +2523,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 break; } -@@ -895,12 +1286,15 @@ static int gpiochip_match_name(struct gpio_chip *gc, void *data) +@@ -895,12 +1294,15 @@ static int gpiochip_match_name(struct gpio_chip *gc, void *data) return !strcmp(gc->label, name); } @@ -2475,7 +2541,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /* * The following is irqchip helper code for gpiochips. -@@ -910,6 +1304,8 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc) +@@ -910,6 +1312,8 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc) { struct gpio_irq_chip *girq = &gc->irq; @@ -2484,7 +2550,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!girq->init_hw) return 0; -@@ -920,6 +1316,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -920,6 +1324,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) { struct gpio_irq_chip *girq = &gc->irq; @@ -2493,7 +2559,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!girq->init_valid_mask) return 0; -@@ -934,6 +1332,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -934,6 +1340,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) { @@ -2502,7 +2568,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 bitmap_free(gc->irq.valid_mask); gc->irq.valid_mask = NULL; } -@@ -941,6 +1341,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) +@@ -941,6 +1349,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, unsigned int offset) { @@ -2511,7 +2577,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!gpiochip_line_is_valid(gc, offset)) return false; /* No mask means all valid */ -@@ -966,6 +1368,8 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, +@@ -966,6 +1376,8 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, struct gpio_irq_chip *girq = &gc->irq; struct device *dev = &gc->gpiodev->dev; @@ -2520,7 +2586,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!girq->domain) { chip_err(gc, "called %s before setting up irqchip\n", __func__); -@@ -1011,7 +1415,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gc, +@@ -1011,7 +1423,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gc, } EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); @@ -2529,7 +2595,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /** * gpiochip_set_hierarchical_irqchip() - connects a hierarchical irqchip -@@ -1250,6 +1654,8 @@ void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, +@@ -1250,6 +1662,8 @@ void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, { struct irq_fwspec *fwspec; @@ -2538,7 +2604,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); if (!fwspec) return NULL; -@@ -1269,6 +1675,8 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, +@@ -1269,6 +1683,8 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, { struct irq_fwspec *fwspec; @@ -2547,7 +2613,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); if (!fwspec) return NULL; -@@ -1296,7 +1704,7 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) +@@ -1296,7 +1712,7 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) return false; } @@ -2556,7 +2622,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /** * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip -@@ -1314,6 +1722,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, +@@ -1314,6 +1730,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, struct gpio_chip *gc = d->host_data; int ret = 0; @@ -2565,7 +2631,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!gpiochip_irqchip_irq_valid(gc, hwirq)) return -ENXIO; -@@ -1415,7 +1825,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) +@@ -1415,7 +1833,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) if (!gpiochip_irqchip_irq_valid(gc, offset)) return -ENXIO; @@ -2574,7 +2640,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (irq_domain_is_hierarchy(domain)) { struct irq_fwspec spec; -@@ -1426,7 +1836,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) +@@ -1426,7 +1844,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) return irq_create_fwspec_mapping(&spec); } @@ -2583,7 +2649,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 return irq_create_mapping(domain, offset); } -@@ -1709,7 +2119,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, +@@ -1709,7 +2127,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, } gc->irq.threaded = threaded; of_node = gc->parent->of_node; @@ -2592,7 +2658,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /* * If the gpiochip has an assigned OF node this takes precedence * FIXME: get rid of this and use gc->parent->of_node -@@ -1717,7 +2127,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, +@@ -1717,7 +2135,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, */ if (gc->of_node) of_node = gc->of_node; @@ -2601,7 +2667,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /* * Specifying a default trigger is a terrible idea if DT or ACPI is * used to configure the interrupts, as you may end-up with -@@ -1796,7 +2206,7 @@ static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -1796,7 +2214,7 @@ static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) { } @@ -2610,7 +2676,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /** * gpiochip_generic_request() - request the gpio function for a pin -@@ -1805,10 +2215,12 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) +@@ -1805,10 +2223,12 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) */ int gpiochip_generic_request(struct gpio_chip *gc, unsigned offset) { @@ -2625,7 +2691,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 return pinctrl_gpio_request(gc->gpiodev->base + offset); } -@@ -1821,10 +2233,12 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); +@@ -1821,10 +2241,12 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); */ void gpiochip_generic_free(struct gpio_chip *gc, unsigned offset) { @@ -2640,7 +2706,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 pinctrl_gpio_free(gc->gpiodev->base + offset); } -@@ -1839,11 +2253,13 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free); +@@ -1839,11 +2261,13 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free); int gpiochip_generic_config(struct gpio_chip *gc, unsigned offset, unsigned long config) { @@ -2655,7 +2721,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /** * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping -@@ -1865,6 +2281,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, +@@ -1865,6 +2289,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, struct gpio_device *gdev = gc->gpiodev; int ret; @@ -2664,7 +2730,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); if (!pin_range) { chip_err(gc, "failed to allocate pin ranges\n"); -@@ -1923,6 +2341,8 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, +@@ -1923,6 +2349,8 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, struct gpio_device *gdev = gc->gpiodev; int ret; @@ -2673,7 +2739,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); if (!pin_range) { chip_err(gc, "failed to allocate pin ranges\n"); -@@ -1964,6 +2384,8 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) +@@ -1964,6 +2392,8 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) struct gpio_pin_range *pin_range, *tmp; struct gpio_device *gdev = gc->gpiodev; @@ -2682,7 +2748,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 list_for_each_entry_safe(pin_range, tmp, &gdev->pin_ranges, node) { list_del(&pin_range->node); pinctrl_remove_gpio_range(pin_range->pctldev, -@@ -1973,7 +2395,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) +@@ -1973,7 +2403,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) } EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); @@ -2691,7 +2757,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /* These "optional" allocation calls help prevent drivers from stomping * on each other, and help provide better diagnostics in debugfs. -@@ -1987,6 +2409,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) +@@ -1987,6 +2417,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) bool hogged = false; unsigned offset; @@ -2700,7 +2766,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (label) { /* Free desc->label if already allocated. */ if (desc->label) { -@@ -2092,6 +2516,8 @@ int gpiod_request(struct gpio_desc *desc, const char *label) +@@ -2092,6 +2524,8 @@ int gpiod_request(struct gpio_desc *desc, const char *label) int ret = -EPROBE_DEFER; struct gpio_device *gdev; @@ -2709,7 +2775,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); gdev = desc->gdev; -@@ -2108,6 +2534,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label) +@@ -2108,6 +2542,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label) return ret; } @@ -2717,7 +2783,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 static bool gpiod_free_commit(struct gpio_desc *desc) { -@@ -2115,6 +2542,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2115,6 +2550,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) unsigned long flags; struct gpio_chip *gc; @@ -2726,7 +2792,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 might_sleep(); gpiod_unexport(desc); -@@ -2141,12 +2570,12 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2141,12 +2578,12 @@ static bool gpiod_free_commit(struct gpio_desc *desc) clear_bit(FLAG_EDGE_RISING, &desc->flags); clear_bit(FLAG_EDGE_FALLING, &desc->flags); clear_bit(FLAG_IS_HOGGED, &desc->flags); @@ -2743,7 +2809,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 ret = true; } -@@ -2159,6 +2588,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2159,6 +2596,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) void gpiod_free(struct gpio_desc *desc) { @@ -2752,7 +2818,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (desc && desc->gdev && gpiod_free_commit(desc)) { module_put(desc->gdev->owner); put_device(&desc->gdev->dev); -@@ -2166,6 +2597,7 @@ void gpiod_free(struct gpio_desc *desc) +@@ -2166,6 +2605,7 @@ void gpiod_free(struct gpio_desc *desc) WARN_ON(extra_checks); } } @@ -2760,7 +2826,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /** * gpiochip_is_requested - return string iff signal was requested -@@ -2184,6 +2616,8 @@ const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned offset) +@@ -2184,6 +2624,8 @@ const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned offset) { struct gpio_desc *desc; @@ -2769,7 +2835,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (offset >= gc->ngpio) return NULL; -@@ -2227,6 +2661,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, +@@ -2227,6 +2669,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, struct gpio_desc *desc = gpiochip_get_desc(gc, hwnum); int ret; @@ -2778,7 +2844,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (IS_ERR(desc)) { chip_err(gc, "failed to get GPIO descriptor\n"); return desc; -@@ -2274,6 +2710,8 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); +@@ -2274,6 +2718,8 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); static int gpio_do_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config) { @@ -2787,7 +2853,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!gc->set_config) return -ENOTSUPP; -@@ -2286,6 +2724,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) +@@ -2286,6 +2732,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) unsigned long config; unsigned arg; @@ -2796,7 +2862,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 switch (mode) { case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_UP: -@@ -2305,6 +2745,8 @@ static int gpio_set_bias(struct gpio_desc *desc) +@@ -2305,6 +2753,8 @@ static int gpio_set_bias(struct gpio_desc *desc) int bias = 0; int ret = 0; @@ -2805,7 +2871,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) bias = PIN_CONFIG_BIAS_DISABLE; else if (test_bit(FLAG_PULL_UP, &desc->flags)) -@@ -2334,6 +2776,8 @@ int gpiod_direction_input(struct gpio_desc *desc) +@@ -2334,6 +2784,8 @@ int gpiod_direction_input(struct gpio_desc *desc) struct gpio_chip *gc; int ret = 0; @@ -2814,7 +2880,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); gc = desc->gdev->chip; -@@ -2381,6 +2825,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) +@@ -2381,6 +2833,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) int val = !!value; int ret = 0; @@ -2823,7 +2889,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /* * It's OK not to specify .direction_output() if the gpiochip is * output-only, but if there is then not even a .set() operation it -@@ -2431,6 +2877,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) +@@ -2431,6 +2885,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) */ int gpiod_direction_output_raw(struct gpio_desc *desc, int value) { @@ -2832,7 +2898,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); return gpiod_direction_output_raw_commit(desc, value); } -@@ -2452,6 +2900,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) +@@ -2452,6 +2908,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) { int ret; @@ -2841,7 +2907,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; -@@ -2523,6 +2973,8 @@ int gpiod_timestamp_control(struct gpio_desc *desc, int enable) +@@ -2523,6 +2981,8 @@ int gpiod_timestamp_control(struct gpio_desc *desc, int enable) { struct gpio_chip *chip; @@ -2850,7 +2916,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); chip = desc->gdev->chip; if (!chip->timestamp_control) { -@@ -2550,6 +3002,8 @@ int gpiod_timestamp_read(struct gpio_desc *desc, u64 *ts) +@@ -2550,6 +3010,8 @@ int gpiod_timestamp_read(struct gpio_desc *desc, u64 *ts) u64 gpio_ts; int ret; @@ -2859,7 +2925,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); chip = desc->gdev->chip; if (!chip->timestamp_read) { -@@ -2578,6 +3032,8 @@ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) +@@ -2578,6 +3040,8 @@ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) { struct gpio_chip *gc; @@ -2868,7 +2934,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); gc = desc->gdev->chip; -@@ -2598,6 +3054,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) +@@ -2598,6 +3062,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) { unsigned long config; @@ -2877,7 +2943,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); return gpiod_set_config(desc, config); } -@@ -2618,6 +3076,8 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) +@@ -2618,6 +3084,8 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) int gpio; int rc; @@ -2886,7 +2952,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); /* * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for -@@ -2652,6 +3112,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_transitory); +@@ -2652,6 +3120,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_transitory); */ int gpiod_is_active_low(const struct gpio_desc *desc) { @@ -2895,7 +2961,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); return test_bit(FLAG_ACTIVE_LOW, &desc->flags); } -@@ -2663,6 +3125,8 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low); +@@ -2663,6 +3133,8 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low); */ void gpiod_toggle_active_low(struct gpio_desc *desc) { @@ -2904,7 +2970,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC_VOID(desc); change_bit(FLAG_ACTIVE_LOW, &desc->flags); } -@@ -2696,6 +3160,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) +@@ -2696,6 +3168,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) int offset; int value; @@ -2913,7 +2979,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 gc = desc->gdev->chip; offset = gpio_chip_hwgpio(desc); value = gc->get ? gc->get(gc, offset) : -EIO; -@@ -2707,6 +3173,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) +@@ -2707,6 +3181,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) static int gpio_chip_get_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { @@ -2922,7 +2988,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (gc->get_multiple) { return gc->get_multiple(gc, mask, bits); } else if (gc->get) { -@@ -2731,6 +3199,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, +@@ -2731,6 +3207,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, { int ret, i = 0; @@ -2931,7 +2997,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /* * Validate array_info against desc_array and its size. * It should immediately follow desc_array if both -@@ -2837,6 +3307,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, +@@ -2837,6 +3315,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, */ int gpiod_get_raw_value(const struct gpio_desc *desc) { @@ -2940,7 +3006,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); /* Should be using gpiod_get_raw_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -2858,6 +3330,8 @@ int gpiod_get_value(const struct gpio_desc *desc) +@@ -2858,6 +3338,8 @@ int gpiod_get_value(const struct gpio_desc *desc) { int value; @@ -2949,7 +3015,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); /* Should be using gpiod_get_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -2892,6 +3366,8 @@ int gpiod_get_raw_array_value(unsigned int array_size, +@@ -2892,6 +3374,8 @@ int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -2958,7 +3024,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(true, false, array_size, -@@ -2918,6 +3394,8 @@ int gpiod_get_array_value(unsigned int array_size, +@@ -2918,6 +3402,8 @@ int gpiod_get_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -2967,7 +3033,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(false, false, array_size, -@@ -2937,6 +3415,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) +@@ -2937,6 +3423,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) struct gpio_chip *gc = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); @@ -2976,7 +3042,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (value) { ret = gc->direction_input(gc, offset); } else { -@@ -2962,6 +3442,8 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value +@@ -2962,6 +3450,8 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value struct gpio_chip *gc = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); @@ -2985,7 +3051,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (value) { ret = gc->direction_output(gc, offset, 1); if (!ret) -@@ -2980,6 +3462,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) +@@ -2980,6 +3470,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) { struct gpio_chip *gc; @@ -2994,7 +3060,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 gc = desc->gdev->chip; trace_gpio_value(desc_to_gpio(desc), 0, value); gc->set(gc, gpio_chip_hwgpio(desc), value); -@@ -2998,6 +3482,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) +@@ -2998,6 +3490,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) static void gpio_chip_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { @@ -3003,7 +3069,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (gc->set_multiple) { gc->set_multiple(gc, mask, bits); } else { -@@ -3017,6 +3503,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, +@@ -3017,6 +3511,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, { int i = 0; @@ -3012,7 +3078,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /* * Validate array_info against desc_array and its size. * It should immediately follow desc_array if both -@@ -3122,6 +3610,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, +@@ -3122,6 +3618,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, */ void gpiod_set_raw_value(struct gpio_desc *desc, int value) { @@ -3021,7 +3087,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_raw_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -3140,6 +3630,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); +@@ -3140,6 +3638,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); */ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) { @@ -3030,7 +3096,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) -@@ -3163,6 +3655,8 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) +@@ -3163,6 +3663,8 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) */ void gpiod_set_value(struct gpio_desc *desc, int value) { @@ -3039,7 +3105,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -3188,6 +3682,8 @@ int gpiod_set_raw_array_value(unsigned int array_size, +@@ -3188,6 +3690,8 @@ int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3048,7 +3114,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(true, false, array_size, -@@ -3213,6 +3709,8 @@ int gpiod_set_array_value(unsigned int array_size, +@@ -3213,6 +3717,8 @@ int gpiod_set_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3057,7 +3123,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(false, false, array_size, -@@ -3228,6 +3726,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); +@@ -3228,6 +3734,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); */ int gpiod_cansleep(const struct gpio_desc *desc) { @@ -3066,7 +3132,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); return desc->gdev->chip->can_sleep; } -@@ -3240,6 +3740,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); +@@ -3240,6 +3748,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); */ int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name) { @@ -3075,7 +3141,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 VALIDATE_DESC(desc); if (name) { name = kstrdup_const(name, GFP_KERNEL); -@@ -3266,6 +3768,8 @@ int gpiod_to_irq(const struct gpio_desc *desc) +@@ -3266,6 +3776,8 @@ int gpiod_to_irq(const struct gpio_desc *desc) struct gpio_chip *gc; int offset; @@ -3084,7 +3150,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /* * Cannot VALIDATE_DESC() here as gpiod_to_irq() consumer semantics * requires this function to not return zero on an invalid descriptor -@@ -3300,6 +3804,8 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq); +@@ -3300,6 +3812,8 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq); int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc; @@ -3093,7 +3159,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 desc = gpiochip_get_desc(gc, offset); if (IS_ERR(desc)) -@@ -3355,6 +3861,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3355,6 +3869,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc; @@ -3102,7 +3168,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 desc = gpiochip_get_desc(gc, offset); if (IS_ERR(desc)) return; -@@ -3372,6 +3880,8 @@ void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3372,6 +3888,8 @@ void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc = gpiochip_get_desc(gc, offset); @@ -3111,7 +3177,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!IS_ERR(desc) && !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); -@@ -3382,6 +3892,8 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3382,6 +3900,8 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc = gpiochip_get_desc(gc, offset); @@ -3120,7 +3186,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!IS_ERR(desc) && !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) { /* -@@ -3397,6 +3909,8 @@ EXPORT_SYMBOL_GPL(gpiochip_enable_irq); +@@ -3397,6 +3917,8 @@ EXPORT_SYMBOL_GPL(gpiochip_enable_irq); bool gpiochip_line_is_irq(struct gpio_chip *gc, unsigned int offset) { @@ -3129,7 +3195,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (offset >= gc->ngpio) return false; -@@ -3430,6 +3944,8 @@ EXPORT_SYMBOL_GPL(gpiochip_relres_irq); +@@ -3430,6 +3952,8 @@ EXPORT_SYMBOL_GPL(gpiochip_relres_irq); bool gpiochip_line_is_open_drain(struct gpio_chip *gc, unsigned int offset) { @@ -3138,7 +3204,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (offset >= gc->ngpio) return false; -@@ -3439,6 +3955,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); +@@ -3439,6 +3963,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); bool gpiochip_line_is_open_source(struct gpio_chip *gc, unsigned int offset) { @@ -3147,7 +3213,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (offset >= gc->ngpio) return false; -@@ -3448,6 +3966,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); +@@ -3448,6 +3974,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); bool gpiochip_line_is_persistent(struct gpio_chip *gc, unsigned int offset) { @@ -3156,7 +3222,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (offset >= gc->ngpio) return false; -@@ -3466,6 +3986,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); +@@ -3466,6 +3994,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); */ int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) { @@ -3165,7 +3231,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 might_sleep_if(extra_checks); VALIDATE_DESC(desc); return gpiod_get_raw_value_commit(desc); -@@ -3485,6 +4007,8 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) +@@ -3485,6 +4015,8 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) { int value; @@ -3174,7 +3240,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 might_sleep_if(extra_checks); VALIDATE_DESC(desc); value = gpiod_get_raw_value_commit(desc); -@@ -3516,6 +4040,8 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, +@@ -3516,6 +4048,8 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3183,7 +3249,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3542,6 +4068,8 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, +@@ -3542,6 +4076,8 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3192,7 +3258,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3563,6 +4091,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); +@@ -3563,6 +4099,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); */ void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { @@ -3201,7 +3267,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 might_sleep_if(extra_checks); VALIDATE_DESC_VOID(desc); gpiod_set_raw_value_commit(desc, value); -@@ -3581,6 +4111,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); +@@ -3581,6 +4119,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); */ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { @@ -3210,7 +3276,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 might_sleep_if(extra_checks); VALIDATE_DESC_VOID(desc); gpiod_set_value_nocheck(desc, value); -@@ -3604,6 +4136,8 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, +@@ -3604,6 +4144,8 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3219,7 +3285,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3621,6 +4155,8 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) +@@ -3621,6 +4163,8 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) { unsigned int i; @@ -3228,7 +3294,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 mutex_lock(&gpio_lookup_lock); for (i = 0; i < n; i++) -@@ -3646,6 +4182,8 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, +@@ -3646,6 +4190,8 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3237,7 +3303,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3661,6 +4199,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); +@@ -3661,6 +4207,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); */ void gpiod_add_lookup_table(struct gpiod_lookup_table *table) { @@ -3246,7 +3312,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 mutex_lock(&gpio_lookup_lock); list_add_tail(&table->list, &gpio_lookup_list); -@@ -3675,6 +4215,8 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); +@@ -3675,6 +4223,8 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); */ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) { @@ -3255,7 +3321,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 mutex_lock(&gpio_lookup_lock); list_del(&table->list); -@@ -3692,6 +4234,8 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) +@@ -3692,6 +4242,8 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) struct gpio_chip *gc; struct gpiod_hog *hog; @@ -3264,7 +3330,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 mutex_lock(&gpio_machine_hogs_mutex); for (hog = &hogs[0]; hog->chip_label; hog++) { -@@ -3715,6 +4259,8 @@ static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) +@@ -3715,6 +4267,8 @@ static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) const char *dev_id = dev ? dev_name(dev) : NULL; struct gpiod_lookup_table *table; @@ -3273,7 +3339,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 mutex_lock(&gpio_lookup_lock); list_for_each_entry(table, &gpio_lookup_list, list) { -@@ -3748,6 +4294,8 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, +@@ -3748,6 +4302,8 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, struct gpiod_lookup_table *table; struct gpiod_lookup *p; @@ -3282,7 +3348,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 table = gpiod_find_lookup_table(dev); if (!table) return desc; -@@ -3813,6 +4361,8 @@ static int platform_gpio_count(struct device *dev, const char *con_id) +@@ -3813,6 +4369,8 @@ static int platform_gpio_count(struct device *dev, const char *con_id) struct gpiod_lookup *p; unsigned int count = 0; @@ -3291,7 +3357,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 table = gpiod_find_lookup_table(dev); if (!table) return -ENOENT; -@@ -3858,6 +4408,8 @@ struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, +@@ -3858,6 +4416,8 @@ struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, char prop_name[32]; /* 32 is max size of property name */ unsigned int i; @@ -3300,7 +3366,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (con_id) snprintf(prop_name, sizeof(prop_name), "%s-%s", -@@ -3886,6 +4438,8 @@ int gpiod_count(struct device *dev, const char *con_id) +@@ -3886,6 +4446,8 @@ int gpiod_count(struct device *dev, const char *con_id) { int count = -ENOENT; @@ -3309,7 +3375,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) count = of_gpio_get_count(dev, con_id); else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) -@@ -3911,6 +4465,8 @@ EXPORT_SYMBOL_GPL(gpiod_count); +@@ -3911,6 +4473,8 @@ EXPORT_SYMBOL_GPL(gpiod_count); struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags) { @@ -3318,7 +3384,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 return gpiod_get_index(dev, con_id, 0, flags); } EXPORT_SYMBOL_GPL(gpiod_get); -@@ -3951,6 +4507,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, +@@ -3951,6 +4515,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, { int ret; @@ -3327,7 +3393,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (lflags & GPIO_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); -@@ -4121,6 +4679,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, +@@ -4121,6 +4687,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, struct gpio_desc *desc = ERR_PTR(-ENODEV); int ret; @@ -3336,7 +3402,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (!fwnode) return ERR_PTR(-EINVAL); -@@ -4178,6 +4738,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, +@@ -4178,6 +4746,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, { struct gpio_desc *desc; @@ -3345,7 +3411,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 desc = gpiod_get_index(dev, con_id, index, flags); if (IS_ERR(desc)) { if (PTR_ERR(desc) == -ENOENT) -@@ -4204,6 +4766,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, +@@ -4204,6 +4774,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, int hwnum; int ret; @@ -3354,7 +3420,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 gc = gpiod_to_chip(desc); hwnum = gpio_chip_hwgpio(desc); -@@ -4235,6 +4799,8 @@ static void gpiochip_free_hogs(struct gpio_chip *gc) +@@ -4235,6 +4807,8 @@ static void gpiochip_free_hogs(struct gpio_chip *gc) { int id; @@ -3363,7 +3429,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 for (id = 0; id < gc->ngpio; id++) { if (test_bit(FLAG_IS_HOGGED, &gc->gpiodev->descs[id].flags)) gpiochip_free_own_desc(&gc->gpiodev->descs[id]); -@@ -4263,6 +4829,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, +@@ -4263,6 +4837,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, struct gpio_chip *gc; int count, bitmap_size; @@ -3372,7 +3438,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 count = gpiod_count(dev, con_id); if (count < 0) return ERR_PTR(count); -@@ -4383,6 +4951,8 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, +@@ -4383,6 +4959,8 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, { struct gpio_descs *descs; @@ -3381,7 +3447,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 descs = gpiod_get_array(dev, con_id, flags); if (PTR_ERR(descs) == -ENOENT) return NULL; -@@ -4399,6 +4969,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); +@@ -4399,6 +4977,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); */ void gpiod_put(struct gpio_desc *desc) { @@ -3390,7 +3456,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 if (desc) gpiod_free(desc); } -@@ -4412,6 +4984,8 @@ void gpiod_put_array(struct gpio_descs *descs) +@@ -4412,6 +4992,8 @@ void gpiod_put_array(struct gpio_descs *descs) { unsigned int i; @@ -3399,7 +3465,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 for (i = 0; i < descs->ndescs; i++) gpiod_put(descs->desc[i]); -@@ -4423,6 +4997,8 @@ static int __init gpiolib_dev_init(void) +@@ -4423,6 +5005,8 @@ static int __init gpiolib_dev_init(void) { int ret; @@ -3408,7 +3474,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 /* Register GPIO sysfs bus */ ret = bus_register(&gpio_bus_type); if (ret < 0) { -@@ -4442,13 +5018,13 @@ static int __init gpiolib_dev_init(void) +@@ -4442,13 +5026,13 @@ static int __init gpiolib_dev_init(void) #if IS_ENABLED(CONFIG_OF_DYNAMIC) && IS_ENABLED(CONFIG_OF_GPIO) WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier)); @@ -3424,7 +3490,7 @@ index 50abb1c20df0..ed57ede9a7cb 100644 static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) { -@@ -4575,4 +5151,4 @@ static int __init gpiolib_debugfs_init(void) +@@ -4575,4 +5159,4 @@ static int __init gpiolib_debugfs_init(void) } subsys_initcall(gpiolib_debugfs_init); diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index dd499eee1..239ad40b0 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -15,7 +15,7 @@ index dcecc9f6e33f..ed7d58d68ba6 100644 endmenu diff --git a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile 2024-05-02 12:06:58.097355696 +0000 -+++ b/drivers/Makefile 2024-05-22 13:27:59.382717623 +0000 ++++ b/drivers/Makefile 2024-06-26 11:51:37.362381438 +0000 @@ -193,0 +194,5 @@ +# +# @@ -48,10 +48,10 @@ index 0000000..2580e02 +obj-$(CONFIG_TEGRA_GPIO_GUEST_PROXY) += gpio-guest-proxy.o diff --git a/drivers/gpio-guest-proxy/gpio-guest-proxy.c b/drivers/gpio-guest-proxy/gpio-guest-proxy.c new file mode 100644 -index 0000000..b1a8cda +index 0000000..849273e --- /dev/null +++ b/drivers/gpio-guest-proxy/gpio-guest-proxy.c -@@ -0,0 +1,796 @@ +@@ -0,0 +1,805 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -112,7 +112,7 @@ index 0000000..b1a8cda + #define deb_verbose(fmt, ...) +#endif + -+#define MEM_SIZE 0x0600 // size of passthrough memory (mem_iova) -- larger than needed ++// MEM_SIZE defined in gpio-host-proxy.h +static volatile void __iomem *mem_iova = NULL; + +extern struct gpio_chip *find_chip_by_name(const char *); @@ -133,60 +133,79 @@ index 0000000..b1a8cda + */ +void guest_chardev_transfer(void *msg, int msg_len, int *generic_return) +{ -+ unsigned char *io_buffer; ++ // unsigned char io_buffer[MEM_SIZE]; + + // deb_debug("\n"); + #ifdef GPIO_DEBUG_VERBOSE -+ hexDump(DEVICE_NAME, "msg", &msg, msg_len); -+ deb_verbose("passthrough signal is: %c", *(char *)msg); ++ deb_verbose("PT transfer signal is: %c", *(char *)msg); ++ hexDump(DEVICE_NAME, "transfer", msg, msg_len); + #endif + ++ /* + // Copy msg, to io_buffer -+ io_buffer = kmalloc(msg_len, GFP_KERNEL); ++ // io_buffer = kmalloc(msg_len, GFP_KERNEL); + memset(io_buffer, 0, msg_len); + memcpy(io_buffer, msg, msg_len); + + // Execute the request by copying the io_buffer + memcpy_toio(mem_iova, io_buffer, msg_len); ++ // kfree(io_buffer); ++ */ + -+ // Read response to io_buffer -+ memcpy_fromio(io_buffer, mem_iova, sizeof(*generic_return)); ++ // Execute the request by copying to io memory ++ deb_verbose("tmp debug mark, length = %d", msg_len); ++ memcpy_toio(mem_iova, msg, msg_len); ++ deb_verbose("PT request value is copied, length = %d", msg_len); + + // check if we expect a return value + if(generic_return) { -+ // Copy reply to io_buffer -+ memcpy(generic_return, io_buffer, sizeof(*generic_return)); -+ deb_verbose("return value %d is copied", *generic_return); ++ // Read response to io_buffer ++ memcpy_fromio(&generic_return, mem_iova, sizeof(*generic_return)); ++ deb_verbose("PT return value %d, is copied", *generic_return); + } ++} ++ ++/* this will not work -- we canot passthrough pointers ++int devm_gpiochip_add_data__redirect(struct device *dev, struct gpio_chip *gc, void *data) { ++ int ret = 0; ++ struct tegra_readl_writel msg; ++ ++ msg.signal = GPIO_ADD_DATA ++ msg.device = dev ++ msg.chip = gc; ++ msg.data = data; // value field is not used + -+ kfree(io_buffer); ++ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ return (u32)ret; +} ++EXPORT_SYMBOL_GPL(devm_gpiochip_add_data__redirect); ++*/ + +// redirect static inline u32 readl_x(const volatile void __iomem *addr) +inline u32 readl_redirect( void * addr, const unsigned char rwltype) { + int ret = 0; -+ struct tegra_readl_writel msg; ++ struct tegra_readl_writel rwlmsg; + -+ msg.signal = GPIO_READL; -+ msg.rwltype = rwltype; -+ msg.address = addr; -+ msg.value = 0; // value field is not used ++ rwlmsg.signal = GPIO_READL; ++ rwlmsg.rwltype = rwltype; ++ rwlmsg.address = addr; ++ rwlmsg.value = 0; // value field is not used + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ guest_chardev_transfer(&rwlmsg, sizeof(rwlmsg), &ret); + return (u32)ret; +} +EXPORT_SYMBOL_GPL(readl_redirect); + +// redirect: static inline void writel_x(u32 value, volatile void __iomem *addr) +inline void writel_redirect( u32 value, void * addr, const unsigned char rwltype) { -+ struct tegra_readl_writel msg; ++ struct tegra_readl_writel rwlmsg; + -+ msg.signal = GPIO_WRITEL; -+ msg.rwltype = rwltype; -+ msg.address = addr; -+ msg.value = value; ++ rwlmsg.signal = GPIO_WRITEL; ++ rwlmsg.rwltype = rwltype; ++ rwlmsg.address = addr; ++ rwlmsg.value = value; + -+ guest_chardev_transfer(&msg, sizeof(msg), NULL); ++ guest_chardev_transfer(&rwlmsg, sizeof(rwlmsg), NULL); +} +EXPORT_SYMBOL_GPL(writel_redirect); + @@ -364,23 +383,6 @@ index 0000000..b1a8cda + return ret; +} + -+/* -+// export of redirection functions -+EXPORT_SYMBOL_GPL(gpiochip_generic_request_redirect); -+EXPORT_SYMBOL_GPL(gpiochip_generic_free_redirect); -+EXPORT_SYMBOL_GPL(tegra186_gpio_get_direction_redirect); -+EXPORT_SYMBOL_GPL(tegra186_gpio_direction_input_redirect); -+EXPORT_SYMBOL_GPL(tegra186_gpio_direction_output_redirect); -+EXPORT_SYMBOL_GPL(tegra186_gpio_get_redirect); -+EXPORT_SYMBOL_GPL(tegra186_gpio_set_redirect); -+EXPORT_SYMBOL_GPL(tegra186_gpio_set_by_name_redirect); -+EXPORT_SYMBOL_GPL(tegra186_gpio_set_config_redirect); -+EXPORT_SYMBOL_GPL(tegra_gpio_timestamp_control_redirect); -+EXPORT_SYMBOL_GPL(tegra_gpio_timestamp_read_redirect); -+EXPORT_SYMBOL_GPL(tegra_gpio_suspend_configure_redirect); -+EXPORT_SYMBOL_GPL(tegra186_gpio_add_pin_ranges_redirect); -+*/ -+ +// unpreserve_all_tegrachips also does unhooking ? +extern void unpreserve_all_tegrachips(void); +struct gpio_chip * find_chip_by_id(int id); @@ -423,7 +425,7 @@ index 0000000..b1a8cda + // Note: gpio is not referenced, the init is agnostic to which chip triggered this function + // In an earlier versio of the code we stored the gpio struct pointer in a static table + // int this_chip_id = gpio.gpio->gpiodev.id; -+ // char this_device[12]; ++ // char this_de:avice[12]; + if(is_set_up) { + deb_error("Attempting to set up guest driver twice\n"); + return -EPERM; @@ -576,6 +578,7 @@ index 0000000..b1a8cda + // We allow tegra_gpio_pt alone or with tegra_gpio_pt_extended (verify later) + if( len != sizeof(struct tegra_gpio_pt) && len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) { + pr_err("Illegal chardev data length. Expected %ld or %ld, got %ld", sizeof(struct tegra_gpio_pt), sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), len); ++ hexDump (DEVICE_NAME, "Chardev (guest) input error", buffer, len); + return -ENOEXEC; + } + @@ -599,16 +602,19 @@ index 0000000..b1a8cda + kfree(kbuf); + return -ENOMEM; + } -+ deb_verbose("kbuf is set up, kbuf=%p", kbuf); ++ // deb_verbose("kbuf is set up, kbuf=%p", kbuf); + + if( len == (sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) ) { + kbuf_ext = (tegra_gpio_pt_extended *)(kbuf + 1); -+ deb_verbose("kbuf_ext is set up kbuf_ext=%p", kbuf_ext); ++ // deb_verbose("kbuf_ext is set up kbuf_ext=%p", kbuf_ext); + } + ++ /* ++ #ifdef GPIO_DEBUG_VERBOSE + // print copied user parameters + hexDump (DEVICE_NAME, "Chardev input", kbuf, len); -+ ++ #endif ++ */ + + // make gpio-host type call to gpio + deb_verbose("Passthrough from guest with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); @@ -623,6 +629,8 @@ index 0000000..b1a8cda + return -ENODEV; + } + chip = find_chip_by_id(kbuf->chipnum); ++ chip_alt = chip; ++/* + #ifdef GPIO_DEBUG_VERBOSE + // chip_alt = find_chip_by_id(kbuf->chipnum); + chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); @@ -631,6 +639,7 @@ index 0000000..b1a8cda + chip = chip_alt; // we assume find_chip_by_name is more reliable + } + #endif ++ */ + if(!chip) { + pr_err("In GPIO_REQ, chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); + kfree(kbuf); @@ -873,10 +882,10 @@ index 0000000..c2e0184 +obj-$(CONFIG_TEGRA_GPIO_HOST_PROXY) += gpio-host-proxy.o diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.c b/drivers/gpio-host-proxy/gpio-host-proxy.c new file mode 100644 -index 0000000..1df0ebb +index 0000000..8c19052 --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.c -@@ -0,0 +1,686 @@ +@@ -0,0 +1,698 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -900,9 +909,9 @@ index 0000000..1df0ebb +#include + +#include "../gpio-host-proxy/gpio-host-proxy.h" -+const unsigned char rwl_std_type = GPIO_RWL_STD; -+const unsigned char rwl_raw_type = GPIO_RWL_RAW; -+const unsigned char rwl_relaxed_type = GPIO_RWL_RELAXED; ++const unsigned char rwl_std_type = RWL_STD; ++const unsigned char rwl_raw_type = RWL_RAW; ++const unsigned char rwl_relaxed_type = RWL_RELAXED; + +#define DEVICE_NAME "gpio-host" // Device name. +#define CLASS_NAME "chardrv" // < The device class -- this is a character device driver @@ -1192,13 +1201,16 @@ index 0000000..1df0ebb +static ssize_t write(struct file *filep, const char *buffer, size_t len, loff_t *offset) +{ + unsigned int ret; -+ unsigned long int ret_l; + struct tegra_gpio_pt *kbuf = NULL; + tegra_gpio_pt_extended *kbuf_ext = NULL; + struct tegra_readl_writel *kbuf_rw = NULL; // used in special case the parameters are for readl and writel passthrough + ++ /* ++ unsigned long int ret_l; + static struct file *file; + static struct inode *inode = NULL; ++ */ ++ + struct gpio_chip *chip; + #ifdef GPIO_DEBUG + struct gpio_chip *chip_alt; @@ -1215,19 +1227,27 @@ index 0000000..1df0ebb + return -EINVAL; + } */ + ++ // DEBUG tmp solution for very long write to chardev ++ goto debug; ++ + // We allow tegra_gpio_pt alone or with tegra_gpio_pt_extended (verify later) + if( len != sizeof(struct tegra_gpio_pt) && \ + len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) && \ + len != sizeof(struct tegra_readl_writel)) { -+ pr_err("Illegal chardev data length. Expected %ld or %ld, got %ld", sizeof(struct tegra_gpio_pt), sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), len); ++ pr_err("Illegal chardev data length. Expected %ld or %ld, got %ld", sizeof(struct tegra_gpio_pt), sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), len); ++ hexDump (DEVICE_NAME, "Chardev (host) input error", buffer, len); + return -ENOEXEC; + } + ++ // DEBUG ++ debug: ++ + if(!offset) { + pr_err("offset pointer is null, ignoring offset\n"); + } + else { + read_buffer += (*offset); ++ return_buffer += (*offset); + } + + kbuf = kmalloc(len, GFP_KERNEL); @@ -1252,23 +1272,23 @@ index 0000000..1df0ebb + deb_verbose("kbuf_ext is set up kbuf_ext=%p", kbuf_ext); + } + -+ // print copied user parameters ++ // print copied user parameters + hexDump (DEVICE_NAME, "Chardev input", kbuf, len); + + // make gpio-host type call to gpio -+ deb_verbose("enter switch with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); ++ deb_verbose("Passthrough in host with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); + + switch (kbuf->signal) { + case GPIO_READL: + kbuf_rw = (struct tegra_readl_writel *)kbuf; + switch (kbuf_rw->rwltype) { -+ case GPIO_RWL_STD: ++ case RWL_STD: + ret = (int)readl(kbuf_rw->address); + break; -+ case GPIO_RWL_RAW: ++ case RWL_RAW: + ret = (int)__raw_readl(kbuf_rw->address); + break; -+ case GPIO_RWL_RELAXED: ++ case RWL_RELAXED: + ret = (int)readl_relaxed(kbuf_rw->address); + break; + } @@ -1277,14 +1297,14 @@ index 0000000..1df0ebb + case GPIO_WRITEL: + kbuf_rw = (struct tegra_readl_writel *)kbuf; + switch (kbuf_rw->rwltype) { -+ case GPIO_RWL_STD: -+ writel(kbuf_rw->value, kbuf_rw->address); ++ case RWL_STD: ++ writel(kbuf_rw->value, kbuf_rw->address); + break; -+ case GPIO_RWL_RAW: -+ __raw_writel(kbuf_rw->value, kbuf_rw->address); ++ case RWL_RAW: ++ __raw_writel(kbuf_rw->value, kbuf_rw->address); + break; -+ case GPIO_RWL_RELAXED: -+ writel_relaxed(kbuf_rw->value, kbuf_rw->address); ++ case RWL_RELAXED: ++ writel_relaxed(kbuf_rw->value, kbuf_rw->address); + break; + } + goto retval; @@ -1408,8 +1428,10 @@ index 0000000..1df0ebb + }; + */ + ++ /* + if(kbuf_ext) { + switch (kbuf->signal) { ++*/ + /* commands to ioctl below (the std gpio chardev) + * not fully implemented + * linehandle_create -- when userspace requests output (called by gpio_ioctl) -- bypasses the chardev @@ -1420,7 +1442,7 @@ index 0000000..1df0ebb + * GPIOHANDLE_SET_CONFIG_IOCTL + * arg: user input or output + */ -+ ++/* + // We could want to use the stock gpio chardev (/dev/gpiochip0 and /dev/gpiochip1) /bc userspace functions use it + // this code is not yet complete and it mey be better to use the stock devices directly. + case GPIO_CHARDEV_OPEN: // .open = gpio_chrdev_open @@ -1506,6 +1528,7 @@ index 0000000..1df0ebb + kfree(kbuf); + return -EFAULT; + }; ++ */ + + goto end; + @@ -1516,8 +1539,6 @@ index 0000000..1df0ebb + return -EFAULT; + }; + -+ goto end; -+ + end: + kfree(kbuf); + return len; @@ -1565,10 +1586,10 @@ index 0000000..1df0ebb +module_exit(gpio_host_proxy_exit); diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.h b/drivers/gpio-host-proxy/gpio-host-proxy.h new file mode 100644 -index 0000000..88ea920 +index 0000000..a473fc0 --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.h -@@ -0,0 +1,102 @@ +@@ -0,0 +1,113 @@ +#ifndef __GPIO_HOST_PROXY__H__ +#define __GPIO_HOST_PROXY__H__ + @@ -1577,25 +1598,24 @@ index 0000000..88ea920 +#include +#include // for gpiod_flags + -+// as a workaround this struct is copied here from drivers/gpio/gpiolib.h -+// including the original header file does not work becaue proxy drivers are overlay and absolute paths will change ++// as a workaround this struct is copied here from drivers/gpio/gpiolib.h in kernel-5.10 ++// including the original header file does not work becaue proxy drivers are in an overlay ++// and relative paths will change (kernel-5.10 will be renamed) ++// #include ../kernel-5.10/drivers/gpio/gpiolib.h +// this copied struct is incomplete and subset of the "real" one +struct gpio_device { + int id; + struct device dev; -+ // a huge number of members are removed here -- do not sizeof this struct !!! ++ // a huge number of members are removed here -- do not "sizeof" this struct !!! +}; + ++// size of qemu iomem. ++// Note: Must be synchronized with value in qemu (hw/misc/nvidia_gpio_guest.c) ++#define MEM_SIZE 0x600 + +/* values to be used as "signal" values in struct tegra_gpio_pt */ -+#define GPIO_CHARDEV_OPEN '1' // .open = gpio_chrdev_open -+#define GPIO_CHARDEV_IOCTL '2' // .unlocked_ioctl = gpio_ioctl -- handles IO operation, get linehandle, set direction -+#define GPIO_CHARDEV_POLL '3' // .poll = lineinfo_watch_poll -+#define GPIO_CHARDEV_READ '4' // .read = lineinfo_watch_read -+#define GPIO_CHARDEV_OWNER '5' // .owner = THIS_MODULE -+#define GPIO_CHARDEV_SEEK '6' // .llseek = no_llseek -+#define GPIO_CHARDEV_RELEASE '7' // .release = gpio_chrdev_release + ++// Note: signals with message size of 8 have a signal value greater than or equal to ascii char 'A' +#define GPIO_SET 's' // set level +#define GPIO_GET 'g' // get level +#define GPIO_GET_DIR 'd' // get direction @@ -1604,7 +1624,7 @@ index 0000000..88ea920 +#define GPIO_CONFIG 'c' // set config +#define GPIO_SET_BY_NAME 'n' // set config + -+#define GPIO_REQ 'r' // generic request ++#define GPIO_REQ 'q' // generic request +#define GPIO_FREE 'f' // free + +#define GPIO_TIMESTAMP_CTRL 'C' // timestamp control @@ -1619,11 +1639,23 @@ index 0000000..88ea920 +#define TEGRA_GPIO_AON_LABEL "tegra234-gpio-aon\x00\x00\x00" // gpio_aon_chip / gpiochip1 --padded to 20 bytes +#define LABEL_SIZE 20 + -+#define GPIO_READL 'R' -+#define GPIO_WRITEL 'W' -+#define GPIO_RWL_STD '0' // general readl/writeL -+#define GPIO_RWL_RAW '1' // __raw assembler version of readl/writel -+#define GPIO_RWL_RELAXED '2' // __relaxed assembler version of readl/writel ++// Note: signals with message size of 16 have a signal value less than ascii char 'A' ++ ++#define GPIO_READL '<' ++#define GPIO_WRITEL '>' ++ ++#define RWL_STD '0' // general readl/writeL ++#define RWL_RAW '1' // __raw assembler version of readl/writel ++#define RWL_RELAXED '2' // __relaxed assembler version of readl/writel ++ // ++#define GPIO_CHARDEV_OPEN '1' // .open = gpio_chrdev_open ++#define GPIO_CHARDEV_IOCTL '2' // .unlocked_ioctl = gpio_ioctl -- handles IO operation, get linehandle, set direction ++#define GPIO_CHARDEV_POLL '3' // .poll = lineinfo_watch_poll ++#define GPIO_CHARDEV_READ '4' // .read = lineinfo_watch_read ++#define GPIO_CHARDEV_OWNER '5' // .owner = THIS_MODULE ++#define GPIO_CHARDEV_SEEK '6' // .llseek = no_llseek ++#define GPIO_CHARDEV_RELEASE '7' // .release = gpio_chrdev_release ++ + +// Note this extern is also in gpio-proxy.h in the kernel source tree (this proxy code might be in an overlay) +extern const unsigned char rwl_std_type; @@ -1632,7 +1664,7 @@ index 0000000..88ea920 + +// sizeof is rounded to even 64 bit passhtough writes -- no need to optimise size further on an aarch64 +struct tegra_readl_writel { -+ unsigned char signal; // Note: signal field is overloaded (based on field offset) with signal in struct tegra_gpio_pt ++ unsigned char signal; // Note: 'signal' field is overlapping (based on field offset) with signal in struct tegra_gpio_pt + unsigned char rwltype; // type of readl/writel call + unsigned char pad[2]; // to get an even mod 64 bit message size + u32 value; @@ -1643,7 +1675,7 @@ index 0000000..88ea920 +struct tegra_gpio_pt { + unsigned char signal; // defines operation + unsigned char chipnum; // number of gpio chip (gpiochip0 or gpiochip1) -+ unsigned char level; // padding to reach 8 byte word alignment ++ unsigned char level; // level to set gpio pin to + unsigned char offset; // address offset for gpio pin + u32 cmd; // gpio_ioctl command + // tegra_gpio_pt_extended p2; // extended parameters -- in second word of struct From ce9e53dfce3aee2a37fefa65529f32716c30383d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Wed, 10 Jul 2024 08:28:04 +0000 Subject: [PATCH 21/26] works fine from /dev/gpio-guest -- readl/writel calls cause host kernel panic --- .../patches/0003-gpio-virt-kernel.patch | 206 ++++----- .../patches/0004-gpio-virt-drivers.patch | 434 +++++++++--------- 2 files changed, 300 insertions(+), 340 deletions(-) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch index f4c17a9b9..71996a445 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch @@ -92,7 +92,7 @@ index 000000000000..5cefb69d83ab + +#endif diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c -index f66fc17faee4..01a24add5f6f 100644 +index f66fc17faee4..9706a77fe5a5 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -58,13 +58,34 @@ @@ -103,8 +103,8 @@ index f66fc17faee4..01a24add5f6f 100644 +// #define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -346,7 +346,7 @@ index f66fc17faee4..01a24add5f6f 100644 } subsys_initcall(tegra_gpio_init); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c -index 5e57824b283e..685ef7b540da 100644 +index 5e57824b283e..ad6b7a66c5ca 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -20,6 +20,46 @@ @@ -357,8 +357,8 @@ index 5e57824b283e..685ef7b540da 100644 +#define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -693,7 +693,7 @@ index 5e57824b283e..685ef7b540da 100644 if (tgi->use_timestamp) { *ts = tegra_gte_read_fifo(tgi, offset); ret = 0; -@@ -645,40 +719,58 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) +@@ -645,40 +719,54 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) void __iomem *base; u32 value; @@ -724,12 +724,10 @@ index 5e57824b283e..685ef7b540da 100644 u32 value; - if (!gpio_is_accessible(gpio, offset)) -- return; -+ deb_verbose("(1) chip %s, Offset %d, Level %d\n", gpio->gpio.label, offset, level); - + if (!gpio_is_accessible(gpio, offset)) { + pr_err("GPIO error: gpio is not accessible, Chip %s, Offset %d", gpio->gpio.label, offset); -+ return; + return; +- + } base = tegra186_gpio_get_base(gpio, offset); + @@ -745,8 +743,6 @@ index 5e57824b283e..685ef7b540da 100644 - writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); + writel_x(value, base + TEGRA186_GPIO_OUTPUT_VALUE); -+ -+ deb_verbose("(2): exiting -- value is %d, base is nn%p\n", value, (void *)base); +} + +void tegra186_gpio_set_by_name(const char *name, unsigned int offset, @@ -760,7 +756,7 @@ index 5e57824b283e..685ef7b540da 100644 } static int tegra186_gpio_set_config(struct gpio_chip *chip, -@@ -689,6 +781,8 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, +@@ -689,6 +777,8 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, u32 debounce, value; void __iomem *base; @@ -769,7 +765,7 @@ index 5e57824b283e..685ef7b540da 100644 base = tegra186_gpio_get_base(gpio, offset); if (base == NULL) return -ENXIO; -@@ -708,15 +802,18 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, +@@ -708,15 +798,18 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, debounce = DIV_ROUND_UP(debounce, USEC_PER_MSEC); value = TEGRA186_GPIO_DEBOUNCE_CONTROL_THRESHOLD(debounce); @@ -791,7 +787,7 @@ index 5e57824b283e..685ef7b540da 100644 static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) { struct tegra_gpio *gpio = gpiochip_get_data(chip); -@@ -725,6 +822,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) +@@ -725,6 +818,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) unsigned int i, j; int err; @@ -800,7 +796,7 @@ index 5e57824b283e..685ef7b540da 100644 if (!gpio->soc->pinmux || gpio->soc->num_pin_ranges == 0) return 0; -@@ -768,6 +867,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, +@@ -768,6 +863,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, struct tegra_gpio *gpio = gpiochip_get_data(chip); unsigned int port, pin, i, offset = 0; @@ -809,7 +805,7 @@ index 5e57824b283e..685ef7b540da 100644 if (WARN_ON(chip->of_gpio_n_cells < 2)) return -EINVAL; -@@ -803,7 +904,7 @@ static void tegra186_irq_ack(struct irq_data *data) +@@ -803,7 +900,7 @@ static void tegra186_irq_ack(struct irq_data *data) if (WARN_ON(base == NULL)) return; @@ -818,7 +814,7 @@ index 5e57824b283e..685ef7b540da 100644 } static void tegra186_irq_mask(struct irq_data *data) -@@ -817,9 +918,9 @@ static void tegra186_irq_mask(struct irq_data *data) +@@ -817,9 +914,9 @@ static void tegra186_irq_mask(struct irq_data *data) if (WARN_ON(base == NULL)) return; @@ -830,7 +826,7 @@ index 5e57824b283e..685ef7b540da 100644 } static void tegra186_irq_unmask(struct irq_data *data) -@@ -833,9 +934,9 @@ static void tegra186_irq_unmask(struct irq_data *data) +@@ -833,9 +930,9 @@ static void tegra186_irq_unmask(struct irq_data *data) if (WARN_ON(base == NULL)) return; @@ -842,7 +838,7 @@ index 5e57824b283e..685ef7b540da 100644 } static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) -@@ -849,7 +950,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) +@@ -849,7 +946,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) if (WARN_ON(base == NULL)) return -ENODEV; @@ -851,7 +847,7 @@ index 5e57824b283e..685ef7b540da 100644 value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK; value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL; -@@ -883,7 +984,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) +@@ -883,7 +980,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) return -EINVAL; } @@ -860,7 +856,7 @@ index 5e57824b283e..685ef7b540da 100644 if ((type & IRQ_TYPE_EDGE_BOTH) == 0) irq_set_handler_locked(data, handle_level_irq); -@@ -930,7 +1031,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) +@@ -930,7 +1027,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) if (j == gpio->num_irqs_per_bank) goto skip; @@ -869,7 +865,7 @@ index 5e57824b283e..685ef7b540da 100644 for_each_set_bit(pin, &value, port->pins) { irq = irq_find_mapping(domain, offset + pin); -@@ -1037,6 +1138,8 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1037,6 +1134,8 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) unsigned int i, j; u32 value; @@ -878,7 +874,7 @@ index 5e57824b283e..685ef7b540da 100644 for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; unsigned int offset, p = port->port; -@@ -1044,7 +1147,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1044,7 +1143,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) base = gpio->secure + port->bank * 0x1000 + 0x800; @@ -887,7 +883,7 @@ index 5e57824b283e..685ef7b540da 100644 /* * For controllers that haven't been locked down yet, make -@@ -1058,7 +1161,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1058,7 +1157,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) */ for (j = 0; j < gpio->num_irqs_per_bank; j++) { dev_dbg(dev, "programming default interrupt routing for port %s\n", @@ -896,7 +892,7 @@ index 5e57824b283e..685ef7b540da 100644 offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j); -@@ -1073,9 +1176,9 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1073,9 +1172,9 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) */ if (j == 0) { @@ -908,7 +904,7 @@ index 5e57824b283e..685ef7b540da 100644 } } } -@@ -1107,6 +1210,184 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +@@ -1107,6 +1206,191 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) return -EINVAL; } @@ -1075,6 +1071,7 @@ index 5e57824b283e..685ef7b540da 100644 + * one doubtful assumption is that chip pointers are numbered by the driver + * in the same order preserve_tegrachip records them */ + inline struct gpio_chip * find_chip_by_id(int id) { ++ /* + int i = 0; + while (atomic_read(&tegra_gpio_hosts_ready) != MAX_CHIP) { + msleep(100); // Sleep briefly instead of looping infinitely. @@ -1083,7 +1080,13 @@ index 5e57824b283e..685ef7b540da 100644 + return NULL; + } + } -+ return &tegra_gpio_hosts[id]->gpio; ++ */ ++ if(id & ~0x00000001) { ++ pr_err("GPIO, *ERROR* Illegal chip number (%d)", id); ++ return 0; ++ } ++ else ++ return &tegra_gpio_hosts[id]->gpio; + } + EXPORT_SYMBOL_GPL(find_chip_by_id); +#endif @@ -1093,7 +1096,7 @@ index 5e57824b283e..685ef7b540da 100644 static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; -@@ -1120,17 +1401,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1120,17 +1404,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) int value; void __iomem *base; @@ -1109,9 +1112,9 @@ index 5e57824b283e..685ef7b540da 100644 + } + + #if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) - -+ deb_debug("GPIO Proxy code\n"); + ++ deb_debug("GPIO Proxy code\n"); + + // If virtual-pa node is defined, it means that we are using a GPIO proxy + err = of_property_read_u64(pdev->dev.of_node, "virtual-pa", &gpio_vpa); + if(!err) { @@ -1138,7 +1141,7 @@ index 5e57824b283e..685ef7b540da 100644 /* count the number of banks in the controller */ for (i = 0; i < gpio->soc->num_ports; i++) -@@ -1139,11 +1443,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1139,11 +1446,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->num_banks++; @@ -1155,7 +1158,7 @@ index 5e57824b283e..685ef7b540da 100644 sizeof(*gpio->gpio_rval), GFP_KERNEL); if (!gpio->gpio_rval) return -ENOMEM; -@@ -1154,9 +1462,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1154,9 +1465,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -EINVAL; } @@ -1168,7 +1171,7 @@ index 5e57824b283e..685ef7b540da 100644 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gte"); if (!res) { dev_err(&pdev->dev, "Missing gte MEM resource\n"); -@@ -1172,14 +1483,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1172,14 +1486,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) } err = platform_irq_count(pdev); @@ -1192,7 +1195,7 @@ index 5e57824b283e..685ef7b540da 100644 gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), GFP_KERNEL); -@@ -1188,27 +1505,43 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1188,27 +1508,43 @@ static int tegra186_gpio_probe(struct platform_device *pdev) for (i = 0; i < gpio->num_irq; i++) { err = platform_get_irq(pdev, i); @@ -1226,7 +1229,7 @@ index 5e57824b283e..685ef7b540da 100644 + if (gpio->use_timestamp) + tegra_gte_setup(gpio); + -+ if( kernel_is_on_guest ) { ++ if(kernel_is_on_guest) { + deb_debug("GPIO Guest init section\n"); + if( ! guest_proxy_is_set_up ) { + ret = tegra_gpio_guest_init(); @@ -1252,7 +1255,7 @@ index 5e57824b283e..685ef7b540da 100644 for (i = 0; i < gpio->soc->num_ports; i++) gpio->gpio.ngpio += gpio->soc->ports[i].pins; -@@ -1229,6 +1562,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1229,6 +1565,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -ENOMEM; names[offset + j] = name; @@ -1260,7 +1263,7 @@ index 5e57824b283e..685ef7b540da 100644 } offset += port->pins; -@@ -1260,6 +1594,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1260,6 +1597,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parent_handler_data = gpio; irq->num_parents = gpio->num_irq; @@ -1269,7 +1272,7 @@ index 5e57824b283e..685ef7b540da 100644 /* * To simplify things, use a single interrupt per bank for now. Some -@@ -1283,8 +1619,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1283,8 +1622,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parents = gpio->irq; } @@ -1285,7 +1288,7 @@ index 5e57824b283e..685ef7b540da 100644 np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (!of_device_is_available(np)) -@@ -1315,9 +1658,36 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1315,9 +1661,36 @@ static int tegra186_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, gpio); @@ -1325,7 +1328,7 @@ index 5e57824b283e..685ef7b540da 100644 if (gpio->soc->is_hw_ts_sup) { for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) { -@@ -1329,24 +1699,25 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1329,24 +1702,25 @@ static int tegra186_gpio_probe(struct platform_device *pdev) if (WARN_ON(base == NULL)) return -EINVAL; @@ -1358,7 +1361,7 @@ index 5e57824b283e..685ef7b540da 100644 static int tegra_gpio_resume_early(struct device *dev) { struct tegra_gpio *gpio = dev_get_drvdata(dev); -@@ -1366,9 +1737,9 @@ static int tegra_gpio_resume_early(struct device *dev) +@@ -1366,9 +1740,9 @@ static int tegra_gpio_resume_early(struct device *dev) regs->restore_needed = false; @@ -1371,7 +1374,7 @@ index 5e57824b283e..685ef7b540da 100644 } return 0; -@@ -1723,7 +2094,9 @@ static struct platform_driver tegra186_gpio_driver = { +@@ -1723,7 +2097,9 @@ static struct platform_driver tegra186_gpio_driver = { .probe = tegra186_gpio_probe, .remove = tegra186_gpio_remove, }; @@ -1696,7 +1699,7 @@ index 30e2476a6dc4..aab40f1cd52f 100644 err = gpio_request_one(array->gpio, array->flags, array->label); if (err) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c -index 647e77db82b1..02f779c4cd65 100644 +index 647e77db82b1..bc7298aa06ce 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -24,6 +24,27 @@ @@ -1707,8 +1710,8 @@ index 647e77db82b1..02f779c4cd65 100644 +#define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -1840,7 +1843,7 @@ index 3ef71ca242ba..bf62aeee66a1 100644 /* If buf is not a number then try to find by name */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c -index 50abb1c20df0..fb9f7b6cfaed 100644 +index 50abb1c20df0..e4648d29f26b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -31,6 +31,27 @@ @@ -1851,8 +1854,8 @@ index 50abb1c20df0..fb9f7b6cfaed 100644 +#define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -4442,63 +4445,8 @@ index c73b34e03aae..4c46d7faac5e 100644 pr_info("initialized pinctrl subsystem\n"); pinctrl_init_debugfs(); return 0; -diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c -index 3fb238714718..a7901c1d85af 100644 ---- a/drivers/pinctrl/devicetree.c -+++ b/drivers/pinctrl/devicetree.c -@@ -106,6 +106,12 @@ struct pinctrl_dev *of_pinctrl_get(struct device_node *np) - } - EXPORT_SYMBOL_GPL(of_pinctrl_get); - -+ -+// DEBUG -+#define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ -+ -+ - static int dt_to_map_one_config(struct pinctrl *p, - struct pinctrl_dev *hog_pctldev, - const char *statename, -@@ -151,6 +157,9 @@ static int dt_to_map_one_config(struct pinctrl *p, - } - of_node_put(np_pctldev); - -+// DEBUG -+deb_debug("# A #"); -+ - /* - * Call pinctrl driver to parse device tree node, and - * generate mapping table entries -@@ -161,7 +170,11 @@ static int dt_to_map_one_config(struct pinctrl *p, - dev_name(pctldev->dev)); - return -ENODEV; - } -+// DEBUG -+deb_debug("# B #"); - ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps); -+// DEBUG -+deb_debug("# C -- OK #"); - if (ret < 0) - return ret; - else if (num_maps == 0) { -@@ -172,11 +185,12 @@ static int dt_to_map_one_config(struct pinctrl *p, - */ - dev_info(p->dev, - "there is not valid maps for state %s\n", statename); -+// DEBUG -+deb_debug("# D -- error #"); - return 0; - } -- -- /* Stash the mapping table chunk away for later use */ -- return dt_remember_or_free_map(p, statename, pctldev, map, num_maps); -+ /* Stash the mapping table chunk away for later use */ -+ return dt_remember_or_free_map(p, statename, pctldev, map, num_maps); - } - - static int dt_remember_dummy_state(struct pinctrl *p, const char *statename) diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c -index f9ecbebe6442..776b1f20a469 100644 +index f9ecbebe6442..e3f313b1fdb1 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -34,14 +34,39 @@ @@ -4509,8 +4457,8 @@ index f9ecbebe6442..776b1f20a469 100644 +//#define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -4875,7 +4823,7 @@ index f9ecbebe6442..776b1f20a469 100644 if (!pmx) return -ENOMEM; diff --git a/drivers/pinctrl/tegra/pinctrl-tegra114.c b/drivers/pinctrl/tegra/pinctrl-tegra114.c -index 135635f4e897..a5da4ec197dc 100644 +index 135635f4e897..050a78ffb0e2 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra114.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra114.c @@ -1845,8 +1845,26 @@ static const struct tegra_pinctrl_soc_data tegra114_pinctrl = { @@ -4886,8 +4834,8 @@ index 135635f4e897..a5da4ec197dc 100644 +// #define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -4914,7 +4862,7 @@ index 135635f4e897..a5da4ec197dc 100644 } arch_initcall(tegra114_pinctrl_init); diff --git a/drivers/pinctrl/tegra/pinctrl-tegra124.c b/drivers/pinctrl/tegra/pinctrl-tegra124.c -index cfc75ca9ae2b..6e5295fdd50e 100644 +index cfc75ca9ae2b..320486fd060b 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra124.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra124.c @@ -2057,8 +2057,26 @@ static const struct tegra_pinctrl_soc_data tegra124_pinctrl = { @@ -4925,8 +4873,8 @@ index cfc75ca9ae2b..6e5295fdd50e 100644 +// #define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -4953,7 +4901,7 @@ index cfc75ca9ae2b..6e5295fdd50e 100644 } arch_initcall(tegra124_pinctrl_init); diff --git a/drivers/pinctrl/tegra/pinctrl-tegra186.c b/drivers/pinctrl/tegra/pinctrl-tegra186.c -index d78447c55527..7df0e6496fad 100644 +index d78447c55527..53139593493d 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra186.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra186.c @@ -918,8 +918,26 @@ static const struct tegra_pinctrl_soc_data tegra186_pinctrl = { @@ -4964,8 +4912,8 @@ index d78447c55527..7df0e6496fad 100644 +// #define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -4999,7 +4947,7 @@ index d78447c55527..7df0e6496fad 100644 } module_exit(tegra186_pinctrl_exit); diff --git a/drivers/pinctrl/tegra/pinctrl-tegra194.c b/drivers/pinctrl/tegra/pinctrl-tegra194.c -index 5c7fa1f1c45f..cfb1fb2e0c2e 100644 +index 5c7fa1f1c45f..c7b6e114f282 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra194.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra194.c @@ -1872,8 +1872,27 @@ static const struct tegra_pinctrl_soc_data tegra194_pinctrl = { @@ -5010,8 +4958,8 @@ index 5c7fa1f1c45f..cfb1fb2e0c2e 100644 +// #define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -5039,7 +4987,7 @@ index 5c7fa1f1c45f..cfb1fb2e0c2e 100644 } arch_initcall(tegra194_pinctrl_init); diff --git a/drivers/pinctrl/tegra/pinctrl-tegra20.c b/drivers/pinctrl/tegra/pinctrl-tegra20.c -index cd605272c068..42f1fe4eb064 100644 +index cd605272c068..acd534f495ff 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra20.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra20.c @@ -2236,6 +2236,23 @@ static const char *cdev2_parents[] = { @@ -5050,8 +4998,8 @@ index cd605272c068..42f1fe4eb064 100644 +// #define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -5083,7 +5031,7 @@ index cd605272c068..42f1fe4eb064 100644 } arch_initcall(tegra20_pinctrl_init); diff --git a/drivers/pinctrl/tegra/pinctrl-tegra210.c b/drivers/pinctrl/tegra/pinctrl-tegra210.c -index 01f200a4c789..12193bd5ffb1 100644 +index 01f200a4c789..8c9dad7e1b5f 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra210.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra210.c @@ -1745,12 +1745,16 @@ static const struct tegra_pinctrl_soc_data tegra210_pinctrl = { @@ -5098,7 +5046,7 @@ index 01f200a4c789..12193bd5ffb1 100644 struct tegra_pingroup *g; int i; -+ printk(KERN_DEBUG "GPIO %s -- file %s", __func__, __FILE__); ++ printk(KERN_DEBUG "GPIO %s -- file %s", __func__, kbasename(__FILE__)); + soc = of_device_get_match_data(&pdev->dev); if (soc->lpdr_support) { @@ -5107,12 +5055,12 @@ index 01f200a4c789..12193bd5ffb1 100644 static int __init tegra210_pinctrl_init(void) { -+ printk(KERN_DEBUG "GPIO %s -- file %s", __func__, __FILE__); ++ printk(KERN_DEBUG "GPIO %s -- file %s", __func__, kbasename(__FILE__)); return platform_driver_register(&tegra210_pinctrl_driver); } arch_initcall(tegra210_pinctrl_init); diff --git a/drivers/pinctrl/tegra/pinctrl-tegra30.c b/drivers/pinctrl/tegra/pinctrl-tegra30.c -index 60e087e5b7f5..eacae723cf11 100644 +index 60e087e5b7f5..1e5f56a4a1e1 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra30.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra30.c @@ -2480,8 +2480,26 @@ static const struct tegra_pinctrl_soc_data tegra30_pinctrl = { @@ -5123,8 +5071,8 @@ index 60e087e5b7f5..eacae723cf11 100644 +// #define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index 239ad40b0..12e2c2b6e 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -15,7 +15,7 @@ index dcecc9f6e33f..ed7d58d68ba6 100644 endmenu diff --git a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile 2024-05-02 12:06:58.097355696 +0000 -+++ b/drivers/Makefile 2024-06-26 11:51:37.362381438 +0000 ++++ b/drivers/Makefile 2024-07-09 14:52:57.660206313 +0000 @@ -193,0 +194,5 @@ +# +# @@ -48,7 +48,7 @@ index 0000000..2580e02 +obj-$(CONFIG_TEGRA_GPIO_GUEST_PROXY) += gpio-guest-proxy.o diff --git a/drivers/gpio-guest-proxy/gpio-guest-proxy.c b/drivers/gpio-guest-proxy/gpio-guest-proxy.c new file mode 100644 -index 0000000..849273e +index 0000000..b128178 --- /dev/null +++ b/drivers/gpio-guest-proxy/gpio-guest-proxy.c @@ -0,0 +1,805 @@ @@ -91,9 +91,9 @@ index 0000000..849273e +#define GPIO_DEBUG_VERBOSE // also activates deb_verbose commands + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__ , __FILE__, ##__VA_ARGS__) -+ #define deb_error(fmt, ...) printk(KERN_ERR "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__ , __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__ , kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_error(fmt, ...) printk(KERN_ERR "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__ , kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -122,6 +122,9 @@ index 0000000..849273e +extern int gpio_outloud; +extern uint64_t gpio_vpa; + ++static char return_buffer[MEM_SIZE]; // using the same size as the input buffer ++static int *return_value = (int *)return_buffer; // local return value for chardev interaction ++ +/* functions redirected to guest-proxy from gpio-tegra186.c + * from setup of gpio_chip in tegra186_gpio_probe + */ @@ -131,68 +134,51 @@ index 0000000..849273e + * mgs: is a pointer to data -- in practice tegra_gpio_pt and tegra_gpio_pt_extended structs + * generic return: poiter to return data, may be NULL if we do not expect a return value + */ -+void guest_chardev_transfer(void *msg, int msg_len, int *generic_return) ++void guest_chardev_transfer(void *msg, char msg_len, int *generic_return) +{ -+ // unsigned char io_buffer[MEM_SIZE]; ++ // encode message length into first byte (LSB bit is preserved for other use) ++ unsigned char *length = (unsigned char *)msg; ++ *length = *length | (unsigned char)msg_len << 1; ++ ++ if(msg_len & 0x80) { ++ deb_error("Illegal message length\n"); // msb will be deleted and must be zero ++ } + + // deb_debug("\n"); + #ifdef GPIO_DEBUG_VERBOSE -+ deb_verbose("PT transfer signal is: %c", *(char *)msg); ++ deb_verbose("PT transfer signal is: %c", *((char *)msg + 1)); + hexDump(DEVICE_NAME, "transfer", msg, msg_len); + #endif + -+ /* -+ // Copy msg, to io_buffer -+ // io_buffer = kmalloc(msg_len, GFP_KERNEL); -+ memset(io_buffer, 0, msg_len); -+ memcpy(io_buffer, msg, msg_len); -+ -+ // Execute the request by copying the io_buffer -+ memcpy_toio(mem_iova, io_buffer, msg_len); -+ // kfree(io_buffer); -+ */ -+ + // Execute the request by copying to io memory -+ deb_verbose("tmp debug mark, length = %d", msg_len); + memcpy_toio(mem_iova, msg, msg_len); + deb_verbose("PT request value is copied, length = %d", msg_len); + ++ // deb_verbose("PT generic_return pointer: 0x%llX\n", (long long int)generic_return); + // check if we expect a return value + if(generic_return) { -+ // Read response to io_buffer -+ memcpy_fromio(&generic_return, mem_iova, sizeof(*generic_return)); -+ deb_verbose("PT return value %d, is copied", *generic_return); -+ } -+} -+ -+/* this will not work -- we canot passthrough pointers -+int devm_gpiochip_add_data__redirect(struct device *dev, struct gpio_chip *gc, void *data) { -+ int ret = 0; -+ struct tegra_readl_writel msg; -+ -+ msg.signal = GPIO_ADD_DATA -+ msg.device = dev -+ msg.chip = gc; -+ msg.data = data; // value field is not used ++ // Read response from io_buffer ++ memcpy_fromio(generic_return, mem_iova, sizeof(*generic_return)); // 32 bits ++ // memcpy_fromio(msg, mem_iova, sizeof(*generic_return)); // copy return value to message buffer 32 bits + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret); -+ return (u32)ret; ++ deb_verbose("PT return value 0x%X, is copied", *generic_return); } +} -+EXPORT_SYMBOL_GPL(devm_gpiochip_add_data__redirect); -+*/ ++ ++_Static_assert(sizeof(u32) == sizeof(int), "return size assertion failed"); + +// redirect static inline u32 readl_x(const volatile void __iomem *addr) +inline u32 readl_redirect( void * addr, const unsigned char rwltype) { -+ int ret = 0; ++ u32 ret = 0; + struct tegra_readl_writel rwlmsg; + + rwlmsg.signal = GPIO_READL; ++ rwlmsg.length = 0; // will be updated later + rwlmsg.rwltype = rwltype; + rwlmsg.address = addr; -+ rwlmsg.value = 0; // value field is not used ++ rwlmsg.value = 0; // value field is not used for readl + + guest_chardev_transfer(&rwlmsg, sizeof(rwlmsg), &ret); -+ return (u32)ret; ++ return ret; +} +EXPORT_SYMBOL_GPL(readl_redirect); + @@ -201,6 +187,7 @@ index 0000000..849273e + struct tegra_readl_writel rwlmsg; + + rwlmsg.signal = GPIO_WRITEL; ++ rwlmsg.length = 0; // will be updated later + rwlmsg.rwltype = rwltype; + rwlmsg.address = addr; + rwlmsg.value = value; @@ -536,10 +523,38 @@ index 0000000..849273e +/* + * Reads from device, displays in userspace, and deletes the read data + */ -+static ssize_t read(struct file *filep, char *buffer, size_t len, loff_t *offset) -+{ -+ deb_info("read stub"); -+ return 0; ++static ssize_t read(struct file *filp, char *buf, size_t len, loff_t *offset) { ++ int remaining_length = sizeof(*return_value) - *offset; ++ ++ deb_info("guest: read gpio chardev\n"); ++ deb_verbose("guest: read op: len = %ld, offset = %lld, *return_value = 0x%X\n", len, *offset, *return_value); ++ ++ if ( remaining_length < 0 ) { ++ deb_info("guest: unrecoverable length *error*, remaining_length = %d\n", remaining_length); ++ return -EINVAL; ++ } ++ ++ if ( len > remaining_length ) { ++ deb_info("guest: recoverable length *error*, len = %ld, remaining_length = %d, rlen = %ld\n", len, remaining_length, sizeof(*return_value)); ++ len = remaining_length - *offset; ++ } ++ ++ if (copy_to_user(buf + *offset, return_buffer + *offset, len)) { ++ deb_info("guest: failed to copy to user\n"); ++ return -EFAULT; ++ } ++ ++ *offset += len; ++ ++ // Check if all data was copied ++ if (sizeof(*return_value) < len) { ++ deb_info("guest: not all bytes were copied\n"); ++ // If not, set the error status and return the number of bytes actually copied ++ // return -EINVAL; ++ } ++ ++ // Otherwise, indicate success by returning the number of bytes requested ++ return len; +} + +/* @@ -552,41 +567,40 @@ index 0000000..849273e + +static ssize_t write(struct file *filep, const char *buffer, size_t len, loff_t *offset) +{ -+ unsigned int ret; -+ unsigned long int ret_l; ++ int ret; ++ // unsigned long int ret_l; + struct tegra_gpio_pt *kbuf = NULL; + tegra_gpio_pt_extended *kbuf_ext = NULL; ++ // unsigned char *mask; + ++ /* + static struct file *file; + static struct inode *inode = NULL; ++ */ + struct gpio_chip *chip; -+ #ifdef GPIO_DEBUG ++ /* ++ #ifdef GPIO_DEBUG_VERBOSE + struct gpio_chip *chip_alt; + #endif ++ */ + -+ char *return_buffer = (char *)buffer; -+ char *read_buffer = (char *)buffer; -+ -+ deb_info("writeing %zu bytes to chardev", len); ++ char *buffer_pos = (char *)buffer; + -+ /* removed because condition is covered by susequent checks on 'len' -+ if (len > 65535) { -+ pr_err("count %zu exceeds max # of bytes allowed, aborting write", len); -+ return -EINVAL; -+ } */ ++ deb_info("## writeing %zu bytes to chardev ##", len); + -+ // We allow tegra_gpio_pt alone or with tegra_gpio_pt_extended (verify later) ++ // We allow tegra_gpio_pt alone or with tegra_gpio_pt_extended + if( len != sizeof(struct tegra_gpio_pt) && len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) { + pr_err("Illegal chardev data length. Expected %ld or %ld, got %ld", sizeof(struct tegra_gpio_pt), sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), len); + hexDump (DEVICE_NAME, "Chardev (guest) input error", buffer, len); + return -ENOEXEC; ++ return 0; + } + + if(!offset) { + pr_err("offset pointer is null, ignoring offset\n"); + } + else { -+ read_buffer += (*offset); ++ buffer_pos += (*offset); + } + + kbuf = kmalloc(len, GFP_KERNEL); @@ -597,121 +611,105 @@ index 0000000..849273e + memset(kbuf, 0, len); + + // Copy header -+ if (copy_from_user(kbuf, read_buffer, sizeof(struct tegra_gpio_pt))) { ++ if (copy_from_user(kbuf, buffer_pos, sizeof(struct tegra_gpio_pt))) { + pr_err("copy_from_user failed\n"); + kfree(kbuf); + return -ENOMEM; + } -+ // deb_verbose("kbuf is set up, kbuf=%p", kbuf); ++ // deb_verbose("kbuf is set up at kbuf=%p", kbuf); + ++ /* not necessary; only present in input to qemu passthough device ++ // mask away length from first byte where it is encoded in the top 7 bits (chipnum is lowest bit) ++ mask = (unsigned char *)kbuf; ++ *mask = *mask && 0x01; ++ */ ++ ++ // we are not checking if tegra_gpio_pt_extended is used, we only check for memory allocation + if( len == (sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) ) { + kbuf_ext = (tegra_gpio_pt_extended *)(kbuf + 1); -+ // deb_verbose("kbuf_ext is set up kbuf_ext=%p", kbuf_ext); ++ deb_verbose("kbuf_ext is set up at kbuf_ext=%p", kbuf_ext); + } + -+ /* -+ #ifdef GPIO_DEBUG_VERBOSE -+ // print copied user parameters ++ // print copied user parameters + hexDump (DEVICE_NAME, "Chardev input", kbuf, len); -+ #endif -+ */ + + // make gpio-host type call to gpio + deb_verbose("Passthrough from guest with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); + ++ chip = find_chip_by_id(kbuf->chipnum); ++ + switch (kbuf->signal) { + case GPIO_REQ: -+ // if(kbuf->chipnum & 0xfe) { // 0 and 1 are allowed values & mask allows fastcheck, marginal save -+ if(kbuf->chipnum >= MAX_CHIP) { // direct copmparison is more future flexible -+ // te!, -+ pr_err("Illegal value for chip number\n"); -+ kfree(kbuf); -+ return -ENODEV; -+ } -+ chip = find_chip_by_id(kbuf->chipnum); -+ chip_alt = chip; -+/* ++ /* + #ifdef GPIO_DEBUG_VERBOSE -+ // chip_alt = find_chip_by_id(kbuf->chipnum); + chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); + if(chip != chip_alt) { + deb_debug("conflicting chip pointers -- primary %p, alternative %p", chip, chip_alt); + chip = chip_alt; // we assume find_chip_by_name is more reliable + } + #endif -+ */ ++ */ + if(!chip) { + pr_err("In GPIO_REQ, chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); + kfree(kbuf); + return -ENODEV; + } -+ -+ deb_verbose("GPIO_REQ, using GPIO chip %s, for device %d, pvalue = %p", chip->label, kbuf->chipnum, chip); ++ deb_verbose("GPIO_REQ, using GPIO chip %s, for device %d\n", chip->label, kbuf->chipnum); + ret = chip->request(chip, kbuf->offset); + goto end; + break; + case GPIO_FREE: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_FREE\n"); + chip->free(chip, kbuf->offset); + // chip_alt = NULL; + goto end; + break; + case GPIO_GET_DIR: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_GET_DIR\n"); + ret = chip->get_direction(chip, kbuf->offset); + goto retval; + break; + case GPIO_SET_IN: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_SET_IN\n"); + ret = chip->direction_input(chip, kbuf->offset); + goto retval; + break; + case GPIO_SET_OUT: -+ chip = find_chip_by_id(kbuf->chipnum); -+ deb_verbose("GPIO_SET_OUT, chip pvalue 0 %p", chip); ++ deb_verbose("GPIO_SET_OUT\n"); + ret = chip->direction_output(chip, kbuf->offset, kbuf->level); + goto retval; + break; + case GPIO_GET: + deb_verbose("GPIO_GET\n"); -+ chip = find_chip_by_id(kbuf->chipnum); + ret = chip->get(chip, kbuf->offset); + goto retval; + break; + case GPIO_SET: -+ chip = find_chip_by_id(kbuf->chipnum); -+ deb_verbose("GPIO_SET, set %d at offset 0x%x in gpiochip %s", kbuf->level, kbuf->offset, chip->label); ++ deb_verbose("GPIO_SET, set %d at offset 0x%x in gpiochip %s\n", kbuf->level, kbuf->offset, chip->label); + chip->set(chip, kbuf->offset, kbuf->level); + goto end; + break; + case GPIO_CONFIG: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_CONFIG\n"); + chip->set_config(chip, kbuf->offset, kbuf_ext->config); // arg mapped to unsigned long config + goto end; + break; + case GPIO_TIMESTAMP_CTRL: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_TIMESTAMP_CTRL\n"); + ret = chip->timestamp_control(chip, kbuf->offset, kbuf->level); // mapping level onto enable + goto retval; + break; + case GPIO_TIMESTAMP_READ: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_TIMESTAMP_READ\n"); -+ ret = chip->timestamp_read(chip, kbuf->offset, (u64 *)return_buffer); // timestamp is u64, return value as pointer ++ ret = chip->timestamp_read(chip, kbuf->offset, (u64 *)buffer_pos); // timestamp is u64, return value as pointer + if(ret) { + pr_err("GPIO_TIMESTAMP_READ error\n"); + goto end; + } -+ // timestamp_read returns value directly to return_buffer ++ // timestamp_read returns value directly to buffer_pos + goto end; + break; + case GPIO_SUSPEND_CONF: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_SUSPEND_CONF\n"); + if(!kbuf_ext) { + pr_err("Parameter error in GPIO_SUSPEND_CONF\n"); @@ -721,7 +719,6 @@ index 0000000..849273e + goto retval; + break; + case GPIO_ADD_PINRANGES: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_ADD_PINRANGES\n"); + ret = chip->add_pin_ranges(chip); + goto retval; @@ -743,8 +740,10 @@ index 0000000..849273e + }; + */ + ++ /* ioctl is exluded from this version + if(kbuf_ext) { + switch (kbuf->signal) { ++ */ + /* commands to ioctl below (the std gpio chardev) + * not fully implemented + * linehandle_create -- when userspace requests output (called by gpio_ioctl) -- bypasses the chardev @@ -755,7 +754,7 @@ index 0000000..849273e + * GPIOHANDLE_SET_CONFIG_IOCTL + * arg: user input or output + */ -+ ++ /* ioctl is exluded from this version + // We could want to use the stock gpio chardev (/dev/gpiochip0 and /dev/gpiochip1) /bc userspace functions use it + // this code is not yet complete and it mey be better to use the stock devices directly. + case GPIO_CHARDEV_OPEN: // .open = gpio_chrdev_open @@ -809,7 +808,7 @@ index 0000000..849273e + return -ENOENT; + } + // defined as: static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, size_t count, loff_t *off) -+ ret = file->f_op->read(file, return_buffer, kbuf_ext->count, NULL); // ++ ret = file->f_op->read(file, buffer_pos, kbuf_ext->count, NULL); // + if (ret) { + pr_err("Reading lineinfo returned zero\n"); + kfree(kbuf); @@ -817,13 +816,11 @@ index 0000000..849273e + } + return -ENXIO; + case GPIO_CHARDEV_OWNER: // .owner = THIS_MODULE -+ if (copy_to_user(return_buffer, file->f_op->owner->name, strlen(file->f_op->owner->name)+1)) { ++ if (copy_to_user(buffer_pos, file->f_op->owner->name, strlen(file->f_op->owner->name)+1)) { + pr_err("GPIO, copying user return value failed\n"); + kfree(kbuf); + return -EFAULT; + } -+ // ret_sz = strlen(file->f_op->owner->name) + 1; -+ // goto generic_ret + break; + default: + pr_err("GPIO, Illegal proxy signal type\n"); @@ -836,17 +833,20 @@ index 0000000..849273e + goto end; + + retlong: -+ if ( copy_to_user(return_buffer, &ret_l, sizeof(ret_l)) ) { -+ pr_err("GPIO, copying int user return value failed\n"); ++ if ( copy_to_user(buffer_pos, &ret_l, sizeof(ret_l)) ) { ++: pr_err("GPIO, copying int user return value failed\n"); + kfree(kbuf); + return -EFAULT; + }; ++ */ + + goto end; + + retval: -+ if ( copy_to_user(return_buffer, &ret, sizeof(ret)) ) { -+ pr_err("GPIO, copying long int user return value failed\n"); ++ *return_value = ret; ++ deb_verbose("retval (guest): 0x%X", ret); ++ if ( copy_to_user((void *)buffer, &ret, sizeof(ret)) ) { ++ pr_err("GPIO, copying int user return value failed\n"); + kfree(kbuf); + return -EFAULT; + }; @@ -882,13 +882,13 @@ index 0000000..c2e0184 +obj-$(CONFIG_TEGRA_GPIO_HOST_PROXY) += gpio-host-proxy.o diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.c b/drivers/gpio-host-proxy/gpio-host-proxy.c new file mode 100644 -index 0000000..8c19052 +index 0000000..42c200e --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.c -@@ -0,0 +1,698 @@ +@@ -0,0 +1,708 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** -+ * NVIDIA GPIO Guest Proxy Kernel Module ++ * NVIDIA GPIO host Proxy Kernel Module + * (c) 2023 Unikie, Oy + * (c) 2023 Kim Sandstrom kim.sandstrom@unikie.com + * @@ -925,8 +925,8 @@ index 0000000..8c19052 +#define GPIO_DEBUG_VERBOSE // also activates deb_verbose commands + +#ifdef GPIO_DEBUG -+ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, __FILE__, ##__VA_ARGS__) ++ #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) @@ -959,6 +959,9 @@ index 0000000..8c19052 +static ssize_t read(struct file *, char *, size_t, loff_t *); +static ssize_t write(struct file *, const char *, size_t, loff_t *); + ++static char return_buffer[MEM_SIZE]; // using the same size as the input buffer ++static int *return_value = (int *)return_buffer; // local return value for chardev interaction ++ +/** + * File operations structure and the functions it points to. + */ @@ -1176,23 +1179,39 @@ index 0000000..8c19052 +/* + * Reads from device, displays in userspace, and deletes the read data + */ -+static ssize_t read(struct file *filep, char *buffer, size_t len, loff_t *offset) -+{ -+ deb_info("read stub\n"); -+ return 0; -+} ++static ssize_t read(struct file *filp, char *buf, size_t len, loff_t *offset) { ++ int remaining_length = sizeof(*return_value) - *offset; + -+// TODO -+/* -+ * Checks if the value to transmit through the -+ * gpio-host is allowed by the device tree configuration -+ */ -+/* -+static bool check_if_allowed(int val) -+{ -+ return false; ++ deb_info("host: read gpio chardev\n"); ++ deb_verbose("host: read op: len = %ld, offset = %lld, *return_value = 0x%X\n", len, *offset, *return_value); ++ ++ if ( remaining_length < 0 ) { ++ deb_info("host: unrecoverable length *error*, remaining_length = %d\n", remaining_length); ++ return -EINVAL; ++ } ++ ++ if ( len > remaining_length ) { ++ deb_info("host: recoverable length *error*, len = %ld, remaining_length = %d, rlen = %ld\n", len, remaining_length, sizeof(*return_value)); ++ len = remaining_length - *offset; ++ } ++ ++ if (copy_to_user(buf + *offset, return_buffer + *offset, len)) { ++ deb_info("host: failed to copy to user\n"); ++ return -EFAULT; ++ } ++ ++ *offset += len; ++ ++ // Check if all data was copied ++ if (sizeof(*return_value) < len) { ++ deb_info("host: not all bytes were copied\n"); ++ // If not, set the error status and return the number of bytes actually copied ++ // return -EINVAL; ++ } ++ ++ // Otherwise, indicate success by returning the number of bytes requested ++ return len; +} -+*/ + +/* + * Writes to the device @@ -1200,54 +1219,44 @@ index 0000000..8c19052 + +static ssize_t write(struct file *filep, const char *buffer, size_t len, loff_t *offset) +{ -+ unsigned int ret; ++ int ret; // 32 bits ++ // unsigned long int ret_l; + struct tegra_gpio_pt *kbuf = NULL; ++ struct tegra_readl_writel *kbuf_rw = NULL; + tegra_gpio_pt_extended *kbuf_ext = NULL; -+ struct tegra_readl_writel *kbuf_rw = NULL; // used in special case the parameters are for readl and writel passthrough ++ // unsigned char *mask; + + /* -+ unsigned long int ret_l; + static struct file *file; + static struct inode *inode = NULL; + */ -+ + struct gpio_chip *chip; + #ifdef GPIO_DEBUG + struct gpio_chip *chip_alt; + #endif + -+ char *return_buffer = (char *)buffer; -+ char *read_buffer = (char *)buffer; -+ -+ deb_info("writeing %zu bytes to chardev", len); -+ -+ /* removed because condition is covered by susequent checks on 'len' -+ if (len > 65535) { -+ pr_err("count %zu exceeds max # of bytes allowed, aborting write", len); -+ return -EINVAL; -+ } */ -+ -+ // DEBUG tmp solution for very long write to chardev -+ goto debug; -+ -+ // We allow tegra_gpio_pt alone or with tegra_gpio_pt_extended (verify later) -+ if( len != sizeof(struct tegra_gpio_pt) && \ -+ len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) && \ -+ len != sizeof(struct tegra_readl_writel)) { -+ pr_err("Illegal chardev data length. Expected %ld or %ld, got %ld", sizeof(struct tegra_gpio_pt), sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), len); -+ hexDump (DEVICE_NAME, "Chardev (host) input error", buffer, len); -+ return -ENOEXEC; ++ char *buffer_pos = (char *)buffer; ++ ++ deb_info("## writeing %zu bytes to chardev ##", len); ++ ++ // We allow tegra_gpio_pt alone or with tegra_gpio_pt_extended ++ if( len != sizeof(struct tegra_gpio_pt) && ++ len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) && ++ len != sizeof(struct tegra_readl_writel) ) { ++ pr_err("Illegal chardev data length. Expected %ld, %ld or %ld, but got %ld\n", ++ sizeof(struct tegra_gpio_pt), ++ sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), ++ sizeof(struct tegra_readl_writel), ++ len); ++ hexDump (DEVICE_NAME, "Chardev (host) input error", buffer, len); ++ return -ENOEXEC; // we dont want kernel panic + } + -+ // DEBUG -+ debug: -+ + if(!offset) { + pr_err("offset pointer is null, ignoring offset\n"); + } + else { -+ read_buffer += (*offset); -+ return_buffer += (*offset); ++ buffer_pos += (*offset); + } + + kbuf = kmalloc(len, GFP_KERNEL); @@ -1258,18 +1267,23 @@ index 0000000..8c19052 + memset(kbuf, 0, len); + + // Copy header -+ if (copy_from_user(kbuf, read_buffer, sizeof(struct tegra_gpio_pt))) { ++ if (copy_from_user(kbuf, buffer_pos, sizeof(struct tegra_gpio_pt))) { + pr_err("copy_from_user failed\n"); + kfree(kbuf); + return -ENOMEM; + } -+ deb_verbose("kbuf is set up, kbuf=%p", kbuf); -+ -+ // return_buffer += len; // ??? we should write return to the base address? no increment? generates new interrupt? ++ // deb_verbose("kbuf is set up at kbuf=%p", kbuf); + ++ /* this should already been done by the passthrough qemu device ++ // mask away length from first byte where it is encoded in the top 7 bits (chipnum is lowest bit) ++ mask = (unsigned char *)kbuf; ++ *mask = *mask && 0x01; ++ */ ++ ++ // we are not checking if tegra_gpio_pt_extended is used, we only check for memory allocation + if( len == (sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) ) { + kbuf_ext = (tegra_gpio_pt_extended *)(kbuf + 1); -+ deb_verbose("kbuf_ext is set up kbuf_ext=%p", kbuf_ext); ++ deb_verbose("kbuf_ext is set up at kbuf_ext=%p", kbuf_ext); + } + + // print copied user parameters @@ -1277,10 +1291,17 @@ index 0000000..8c19052 + + // make gpio-host type call to gpio + deb_verbose("Passthrough in host with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); ++ /* ++ #ifdef GPIO_DEBUG ++ deb_verbose("Debug abort\n"); ++ return 0; ++ #endif ++ */ + + switch (kbuf->signal) { + case GPIO_READL: + kbuf_rw = (struct tegra_readl_writel *)kbuf; ++ deb_verbose("readl debug A\n"); + switch (kbuf_rw->rwltype) { + case RWL_STD: + ret = (int)readl(kbuf_rw->address); @@ -1292,10 +1313,12 @@ index 0000000..8c19052 + ret = (int)readl_relaxed(kbuf_rw->address); + break; + } -+ goto end; ++ deb_verbose("readl debug B\n"); ++ goto retval; + break; + case GPIO_WRITEL: + kbuf_rw = (struct tegra_readl_writel *)kbuf; ++ deb_verbose("writel debug A\n"); + switch (kbuf_rw->rwltype) { + case RWL_STD: + writel(kbuf_rw->value, kbuf_rw->address); @@ -1306,20 +1329,18 @@ index 0000000..8c19052 + case RWL_RELAXED: + writel_relaxed(kbuf_rw->value, kbuf_rw->address); + break; ++ deb_verbose("writel debug B\n"); ++ goto end; + } -+ goto retval; + break; ++ } ++ // if switch above is triggered we will either goto retval or goto end ++ ++ chip = find_chip_by_id(kbuf->chipnum); ++ ++ switch (kbuf->signal) { + case GPIO_REQ: -+ // if(kbuf->chipnum & 0xfe) { // 0 and 1 are allowed values & mask allows fastcheck, marginal save -+ if(kbuf->chipnum >= MAX_CHIP) { // direct copmparison is more future flexible -+ // te!, -+ pr_err("Illegal value for chip number\n"); -+ kfree(kbuf); -+ return -ENODEV; -+ } -+ chip = find_chip_by_id(kbuf->chipnum); + #ifdef GPIO_DEBUG_VERBOSE -+ // chip_alt = find_chip_by_id(kbuf->chipnum); + chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); + if(chip != chip_alt) { + deb_debug("conflicting chip pointers -- primary %p, alternative %p", chip, chip_alt); @@ -1331,72 +1352,62 @@ index 0000000..8c19052 + kfree(kbuf); + return -ENODEV; + } -+ deb_verbose("GPIO_REQ, using GPIO chip %s, for device %d, pvalue = %p", chip->label, kbuf->chipnum, chip); ++ deb_verbose("GPIO_REQ, using GPIO chip %s, for device %d\n", chip->label, kbuf->chipnum); + ret = chip->request(chip, kbuf->offset); + goto end; + break; + case GPIO_FREE: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_FREE\n"); + chip->free(chip, kbuf->offset); + // chip_alt = NULL; + goto end; + break; + case GPIO_GET_DIR: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_GET_DIR\n"); + ret = chip->get_direction(chip, kbuf->offset); + goto retval; + break; + case GPIO_SET_IN: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_SET_IN\n"); + ret = chip->direction_input(chip, kbuf->offset); + goto retval; + break; + case GPIO_SET_OUT: -+ chip = find_chip_by_id(kbuf->chipnum); -+ deb_verbose("GPIO_SET_OUT, chip pvalue 0 %p", chip); ++ deb_verbose("GPIO_SET_OUT\n"); + ret = chip->direction_output(chip, kbuf->offset, kbuf->level); + goto retval; + break; + case GPIO_GET: + deb_verbose("GPIO_GET\n"); -+ chip = find_chip_by_id(kbuf->chipnum); + ret = chip->get(chip, kbuf->offset); + goto retval; + break; + case GPIO_SET: -+ chip = find_chip_by_id(kbuf->chipnum); -+ deb_verbose("GPIO_SET, set %d at offset 0x%x in gpiochip %s", kbuf->level, kbuf->offset, chip->label); ++ deb_verbose("GPIO_SET, set %d at offset 0x%x in gpiochip %s\n", kbuf->level, kbuf->offset, chip->label); + chip->set(chip, kbuf->offset, kbuf->level); + goto end; + break; + case GPIO_CONFIG: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_CONFIG\n"); + chip->set_config(chip, kbuf->offset, kbuf_ext->config); // arg mapped to unsigned long config + goto end; + break; + case GPIO_TIMESTAMP_CTRL: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_TIMESTAMP_CTRL\n"); + ret = chip->timestamp_control(chip, kbuf->offset, kbuf->level); // mapping level onto enable + goto retval; + break; + case GPIO_TIMESTAMP_READ: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_TIMESTAMP_READ\n"); -+ ret = chip->timestamp_read(chip, kbuf->offset, (u64 *)return_buffer); // timestamp is u64, return value as pointer ++ ret = chip->timestamp_read(chip, kbuf->offset, (u64 *)buffer_pos); // timestamp is u64, return value as pointer + if(ret) { + pr_err("GPIO_TIMESTAMP_READ error\n"); + goto end; + } -+ // timestamp_read returns value directly to return_buffer ++ // timestamp_read returns value directly to buffer_pos + goto end; + break; + case GPIO_SUSPEND_CONF: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_SUSPEND_CONF\n"); + if(!kbuf_ext) { + pr_err("Parameter error in GPIO_SUSPEND_CONF\n"); @@ -1406,7 +1417,6 @@ index 0000000..8c19052 + goto retval; + break; + case GPIO_ADD_PINRANGES: -+ chip = find_chip_by_id(kbuf->chipnum); + deb_verbose("GPIO_ADD_PINRANGES\n"); + ret = chip->add_pin_ranges(chip); + goto retval; @@ -1484,7 +1494,7 @@ index 0000000..8c19052 + pr_err("GPIO, chardev file was expected to be open\n"); + kfree(kbuf); + return -ENOENT; -+ } ++ }) + // defined as: static __poll_t lineinfo_watch_poll(struct file *file, struct poll_table_struct *pollt) + ret = file->f_op->poll(file, kbuf_ext->poll); // TODO arg is pointer data which should have been copied + goto retval; // __poll_t is of size unsigned int @@ -1496,7 +1506,7 @@ index 0000000..8c19052 + return -ENOENT; + } + // defined as: static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, size_t count, loff_t *off) -+ ret = file->f_op->read(file, return_buffer, kbuf_ext->count, NULL); // ++ ret = file->f_op->read(file, buffer_pos, kbuf_ext->count, NULL); // + if (ret) { + pr_err("Reading lineinfo returned zero\n"); + kfree(kbuf); @@ -1504,13 +1514,11 @@ index 0000000..8c19052 + } + return -ENXIO; + case GPIO_CHARDEV_OWNER: // .owner = THIS_MODULE -+ if (copy_to_user(return_buffer, file->f_op->owner->name, strlen(file->f_op->owner->name)+1)) { ++ if (copy_to_user(buffer_pos, file->f_op->owner->name, strlen(file->f_op->owner->name)+1)) { + pr_err("GPIO, copying user return value failed\n"); + kfree(kbuf); + return -EFAULT; + } -+ // ret_sz = strlen(file->f_op->owner->name) + 1; -+ // goto generic_ret + break; + default: + pr_err("GPIO, Illegal proxy signal type\n"); @@ -1523,7 +1531,7 @@ index 0000000..8c19052 + goto end; + + retlong: -+ if ( copy_to_user(return_buffer, &ret_l, sizeof(ret_l)) ) { ++ if ( copy_to_user(buffer_pos, &ret_l, sizeof(ret_l)) ) { + pr_err("GPIO, copying int user return value failed\n"); + kfree(kbuf); + return -EFAULT; @@ -1533,15 +1541,17 @@ index 0000000..8c19052 + goto end; + + retval: -+ if ( copy_to_user(return_buffer, &ret, sizeof(ret)) ) { -+ pr_err("GPIO, copying long int user return value failed\n"); ++ *return_value = ret; ++ deb_verbose("retval (host): 0x%X", ret); ++ if ( copy_to_user((void *)buffer, &ret, sizeof(ret)) ) { ++ pr_err("GPIO, copying unsigned int user return value failed\n"); + kfree(kbuf); + return -EFAULT; + }; + + end: + kfree(kbuf); -+ return len; ++ return len; // return length of read data +} + +/* module creation -- see also gpio_host_proxy_probe and gpio_host_proxy_remove */ @@ -1586,10 +1596,10 @@ index 0000000..8c19052 +module_exit(gpio_host_proxy_exit); diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.h b/drivers/gpio-host-proxy/gpio-host-proxy.h new file mode 100644 -index 0000000..a473fc0 +index 0000000..55cdbaa --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.h -@@ -0,0 +1,113 @@ +@@ -0,0 +1,115 @@ +#ifndef __GPIO_HOST_PROXY__H__ +#define __GPIO_HOST_PROXY__H__ + @@ -1611,7 +1621,7 @@ index 0000000..a473fc0 + +// size of qemu iomem. +// Note: Must be synchronized with value in qemu (hw/misc/nvidia_gpio_guest.c) -+#define MEM_SIZE 0x600 ++#define MEM_SIZE 0x80 + +/* values to be used as "signal" values in struct tegra_gpio_pt */ + @@ -1624,7 +1634,7 @@ index 0000000..a473fc0 +#define GPIO_CONFIG 'c' // set config +#define GPIO_SET_BY_NAME 'n' // set config + -+#define GPIO_REQ 'q' // generic request ++#define GPIO_REQ 'r' // generic request +#define GPIO_FREE 'f' // free + +#define GPIO_TIMESTAMP_CTRL 'C' // timestamp control @@ -1664,42 +1674,44 @@ index 0000000..a473fc0 + +// sizeof is rounded to even 64 bit passhtough writes -- no need to optimise size further on an aarch64 +struct tegra_readl_writel { ++ unsigned char length; // shift right one bit, most LSB is ignored + unsigned char signal; // Note: 'signal' field is overlapping (based on field offset) with signal in struct tegra_gpio_pt -+ unsigned char rwltype; // type of readl/writel call -+ unsigned char pad[2]; // to get an even mod 64 bit message size ++ unsigned char rwltype; // type of readl/writel call; std, raw, relaxed ++ unsigned char pad[1]; // to get no word wrap at mod 64 bit message size + u32 value; + void * address; +}; + +// struct __attribute__((packed)) tegra_gpio_pt { +struct tegra_gpio_pt { ++ unsigned char chipnum; // lowest bit is number of gpio chip (gpiochip0 or gpiochip1), top 7 bits are message length + unsigned char signal; // defines operation -+ unsigned char chipnum; // number of gpio chip (gpiochip0 or gpiochip1) + unsigned char level; // level to set gpio pin to + unsigned char offset; // address offset for gpio pin -+ u32 cmd; // gpio_ioctl command ++ // u32 cmd; // gpio_ioctl command + // tegra_gpio_pt_extended p2; // extended parameters -- in second word of struct +}; + -+typedef union extended { ++union extended { + // int level; // pin level to be set + unsigned long config; // pin configuration + int enable; + size_t count; // lineinfo read size + struct poll_table_struct *poll; -+ enum gpiod_flags dflags ; ++ enum gpiod_flags dflags; + u64 arg; // gpio_ioctl argument (this is interpreted as a pointer) -+} tegra_gpio_pt_extended; ++}; ++ ++typedef union extended tegra_gpio_pt_extended; + +#define MAX_CHIP 2 + +_Static_assert( sizeof(struct tegra_readl_writel) == 16, + "tegra_readl_writel size is not 16 bytes." ); + -+_Static_assert( sizeof(struct tegra_gpio_pt) == 8, -+ "tegra_gpio_pt size is not 8 bytes." ); ++_Static_assert( sizeof(struct tegra_gpio_pt) == 4, ++ "tegra_gpio_pt size is not 4 bytes." ); + +_Static_assert( sizeof(tegra_gpio_pt_extended) == 8, + "tegra_gpio_pt_extended size is not 8 bytes." ); -+ +#endif From e558becf42f368d9d39280e55a01215fb3d2b43a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Fri, 12 Jul 2024 06:28:37 +0000 Subject: [PATCH 22/26] Gpio works from chardev devices in guest and gpio-host-proxy - New baseline for adding more passthrough functions - ./simple-chardev-test.sh works in /dev/gpio-host and /dev/gpio.guest - readl/writel execution blocked in host - Guest kernel is not aware of pins in host (gpioinfo gives false data) - use qemu: 8c5b0acccd3b4f47ba84d23b628ef294533e662a - kernel: e3a4e96cd08ab8d1a31498a10f8f7320ef53bd9d/2d96cc2e8e1c4ee4a3e973023b6c398a98dc0d93 - precompiled as nix generation 233 --- .../patches/0003-gpio-virt-kernel.patch | 58 ++++++++++----- .../patches/0004-gpio-virt-drivers.patch | 74 +++++++++---------- 2 files changed, 75 insertions(+), 57 deletions(-) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch index 71996a445..55323fc1e 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch @@ -346,7 +346,7 @@ index f66fc17faee4..9706a77fe5a5 100644 } subsys_initcall(tegra_gpio_init); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c -index 5e57824b283e..ad6b7a66c5ca 100644 +index 5e57824b283e..de08bdbf435a 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -20,6 +20,46 @@ @@ -904,7 +904,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 } } } -@@ -1107,6 +1206,191 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +@@ -1107,6 +1206,211 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) return -EINVAL; } @@ -939,6 +939,16 @@ index 5e57824b283e..ad6b7a66c5ca 100644 + extern int tegra186_gpio_set_config_redirect(struct gpio_chip *chip, + unsigned int offset, + unsigned long config); ++ /* ++ extern const struct tegra_gpio_port * ++ tegra186_gpio_get_port_redirect(struct tegra_gpio *gpio, unsigned int *pin); ++ ++ extern void __iomem *tegra186_gpio_get_base_redirect(struct tegra_gpio *gpio, ++ unsigned int pin); ++ ++ extern void __iomem *tegra186_gpio_get_secure_redirect(struct tegra_gpio *gpio, ++ unsigned int pin); ++ */ + + extern int tegra_gpio_timestamp_control_redirect(struct gpio_chip *chip, unsigned offset, + int enable); @@ -959,13 +969,20 @@ index 5e57824b283e..ad6b7a66c5ca 100644 + gpio->gpio.direction_input = tegra186_gpio_direction_input_redirect; + gpio->gpio.direction_output = tegra186_gpio_direction_output_redirect; + gpio->gpio.get = tegra186_gpio_get_redirect; ++ // gpio->gpio.get_multiple = N/A; + gpio->gpio.set = tegra186_gpio_set_redirect; ++ // gpio->gpio.set_multiple = N/A; + gpio->gpio.set_config = tegra186_gpio_set_config_redirect; ++ // gpio->gpio.set_config = tegra186_gpio_get_port_redirect; // not in struct ++ // gpio->gpio.set_config = tegra186_gpio_get_base_redirect; // not in struct ++ // gpio->gpio.set_config = tegra186_gpio_get_secure_redirect; // not in struct + gpio->gpio.timestamp_control = tegra_gpio_timestamp_control_redirect; + gpio->gpio.timestamp_read = tegra_gpio_timestamp_read_redirect; + gpio->gpio.suspend_configure = tegra_gpio_suspend_configure_redirect; -+ // DEBUG -+ // gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges_redirect; ++ // gpio->gpio.to_irq = N/A; ++ // gpio->gpio.dbg_show = N/A; ++ // gpio->gpio.init_valid_mask = N/A; ++ // gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges_redirect; + gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; + gpio->gpio.base = -1; + } @@ -985,6 +1002,9 @@ index 5e57824b283e..ad6b7a66c5ca 100644 + gpio->gpio.timestamp_control = tegra_gpio_timestamp_control; + gpio->gpio.timestamp_read = tegra_gpio_timestamp_read; + gpio->gpio.suspend_configure = tegra_gpio_suspend_configure; ++ // gpio->gpio.to_irq = N/A; ++ // gpio->gpio.dbg_show = N/A; ++ // gpio->gpio.init_valid_mask = N/A; + gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; + gpio->gpio.base = -1; +} @@ -1096,7 +1116,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; -@@ -1120,17 +1404,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1120,17 +1424,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) int value; void __iomem *base; @@ -1141,7 +1161,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 /* count the number of banks in the controller */ for (i = 0; i < gpio->soc->num_ports; i++) -@@ -1139,11 +1446,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1139,11 +1466,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->num_banks++; @@ -1158,7 +1178,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 sizeof(*gpio->gpio_rval), GFP_KERNEL); if (!gpio->gpio_rval) return -ENOMEM; -@@ -1154,9 +1465,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1154,9 +1485,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -EINVAL; } @@ -1171,7 +1191,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gte"); if (!res) { dev_err(&pdev->dev, "Missing gte MEM resource\n"); -@@ -1172,14 +1486,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1172,14 +1506,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) } err = platform_irq_count(pdev); @@ -1195,7 +1215,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), GFP_KERNEL); -@@ -1188,27 +1508,43 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1188,27 +1528,43 @@ static int tegra186_gpio_probe(struct platform_device *pdev) for (i = 0; i < gpio->num_irq; i++) { err = platform_get_irq(pdev, i); @@ -1255,7 +1275,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 for (i = 0; i < gpio->soc->num_ports; i++) gpio->gpio.ngpio += gpio->soc->ports[i].pins; -@@ -1229,6 +1565,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1229,6 +1585,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -ENOMEM; names[offset + j] = name; @@ -1263,7 +1283,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 } offset += port->pins; -@@ -1260,6 +1597,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1260,6 +1617,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parent_handler_data = gpio; irq->num_parents = gpio->num_irq; @@ -1272,7 +1292,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 /* * To simplify things, use a single interrupt per bank for now. Some -@@ -1283,8 +1622,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1283,8 +1642,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parents = gpio->irq; } @@ -1288,7 +1308,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (!of_device_is_available(np)) -@@ -1315,9 +1661,36 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1315,9 +1681,36 @@ static int tegra186_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, gpio); @@ -1328,7 +1348,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 if (gpio->soc->is_hw_ts_sup) { for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) { -@@ -1329,24 +1702,25 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1329,24 +1722,25 @@ static int tegra186_gpio_probe(struct platform_device *pdev) if (WARN_ON(base == NULL)) return -EINVAL; @@ -1350,9 +1370,9 @@ index 5e57824b283e..ad6b7a66c5ca 100644 - - return 0; + #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) -+ preserve_tegrachip(gpio); -+ // guest_skip: -+ #endif ++ preserve_tegrachip(gpio); ++ // guest_skip: ++ #endif + return 0; } @@ -1361,7 +1381,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 static int tegra_gpio_resume_early(struct device *dev) { struct tegra_gpio *gpio = dev_get_drvdata(dev); -@@ -1366,9 +1740,9 @@ static int tegra_gpio_resume_early(struct device *dev) +@@ -1366,9 +1760,9 @@ static int tegra_gpio_resume_early(struct device *dev) regs->restore_needed = false; @@ -1374,7 +1394,7 @@ index 5e57824b283e..ad6b7a66c5ca 100644 } return 0; -@@ -1723,7 +2097,9 @@ static struct platform_driver tegra186_gpio_driver = { +@@ -1723,7 +2117,9 @@ static struct platform_driver tegra186_gpio_driver = { .probe = tegra186_gpio_probe, .remove = tegra186_gpio_remove, }; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index 12e2c2b6e..530b47aae 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -15,7 +15,7 @@ index dcecc9f6e33f..ed7d58d68ba6 100644 endmenu diff --git a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile 2024-05-02 12:06:58.097355696 +0000 -+++ b/drivers/Makefile 2024-07-09 14:52:57.660206313 +0000 ++++ b/drivers/Makefile 2024-07-11 13:56:04.938122389 +0000 @@ -193,0 +194,5 @@ +# +# @@ -48,10 +48,10 @@ index 0000000..2580e02 +obj-$(CONFIG_TEGRA_GPIO_GUEST_PROXY) += gpio-guest-proxy.o diff --git a/drivers/gpio-guest-proxy/gpio-guest-proxy.c b/drivers/gpio-guest-proxy/gpio-guest-proxy.c new file mode 100644 -index 0000000..b128178 +index 0000000..6b385ed --- /dev/null +++ b/drivers/gpio-guest-proxy/gpio-guest-proxy.c -@@ -0,0 +1,805 @@ +@@ -0,0 +1,794 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -144,24 +144,19 @@ index 0000000..b128178 + deb_error("Illegal message length\n"); // msb will be deleted and must be zero + } + -+ // deb_debug("\n"); -+ #ifdef GPIO_DEBUG_VERBOSE -+ deb_verbose("PT transfer signal is: %c", *((char *)msg + 1)); -+ hexDump(DEVICE_NAME, "transfer", msg, msg_len); -+ #endif ++ deb_verbose("PT transfer signal is: %c", *((char *)msg + 1)); ++ hexDump(DEVICE_NAME, "PT transfer (to host)", msg, msg_len); + + // Execute the request by copying to io memory + memcpy_toio(mem_iova, msg, msg_len); -+ deb_verbose("PT request value is copied, length = %d", msg_len); ++ // deb_verbose("PT request value is copied, length = %d", msg_len); + + // deb_verbose("PT generic_return pointer: 0x%llX\n", (long long int)generic_return); + // check if we expect a return value + if(generic_return) { + // Read response from io_buffer + memcpy_fromio(generic_return, mem_iova, sizeof(*generic_return)); // 32 bits -+ // memcpy_fromio(msg, mem_iova, sizeof(*generic_return)); // copy return value to message buffer 32 bits -+ -+ deb_verbose("PT return value 0x%X, is copied", *generic_return); } ++ hexDump(DEVICE_NAME, "PT transfer (from host)", generic_return, sizeof(*generic_return) ); } +} + +_Static_assert(sizeof(u32) == sizeof(int), "return size assertion failed"); @@ -178,6 +173,7 @@ index 0000000..b128178 + rwlmsg.value = 0; // value field is not used for readl + + guest_chardev_transfer(&rwlmsg, sizeof(rwlmsg), &ret); ++ deb_verbose("return value: 0x%X", ret); + return ret; +} +EXPORT_SYMBOL_GPL(readl_redirect); @@ -746,18 +742,11 @@ index 0000000..b128178 + */ + /* commands to ioctl below (the std gpio chardev) + * not fully implemented -+ * linehandle_create -- when userspace requests output (called by gpio_ioctl) -- bypasses the chardev -+ * linehandle_ioctl -- linehandle_ioctl when userspace does actual io (toggles pin) -+ * cmd: -+ * GPIOHANDLE_GET_LINE_VALUES_IOCTL, -+ * GPIOHANDLE_SET_LINE_VALUES_IOCTL, -+ * GPIOHANDLE_SET_CONFIG_IOCTL -+ * arg: user input or output -+ */ -+ /* ioctl is exluded from this version -+ // We could want to use the stock gpio chardev (/dev/gpiochip0 and /dev/gpiochip1) /bc userspace functions use it -+ // this code is not yet complete and it mey be better to use the stock devices directly. -+ case GPIO_CHARDEV_OPEN: // .open = gpio_chrdev_open ++ file = filp_open(tegra_chiplabel[kbuf->chipnum], O_RDWR, 0); ++ if (IS_ERR(file)) { ++ pr_err("GPIO, failed to open chardev for chip %s: %ld", tegra_chiplabel[kbuf->chipnum], PTR_ERR(file)); ++ kfree(kbuf); ++ return -ENOENT; + file = filp_open(tegra_chiplabel[kbuf->chipnum], O_RDWR, 0); + if (IS_ERR(file)) { + pr_err("GPIO, failed to open chardev for chip %s: %ld", tegra_chiplabel[kbuf->chipnum], PTR_ERR(file)); @@ -882,10 +871,10 @@ index 0000000..c2e0184 +obj-$(CONFIG_TEGRA_GPIO_HOST_PROXY) += gpio-host-proxy.o diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.c b/drivers/gpio-host-proxy/gpio-host-proxy.c new file mode 100644 -index 0000000..42c200e +index 0000000..b26862b --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.c -@@ -0,0 +1,708 @@ +@@ -0,0 +1,717 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO host Proxy Kernel Module @@ -1232,7 +1221,7 @@ index 0000000..42c200e + */ + struct gpio_chip *chip; + #ifdef GPIO_DEBUG -+ struct gpio_chip *chip_alt; ++ // struct gpio_chip *chip_alt; + #endif + + char *buffer_pos = (char *)buffer; @@ -1291,17 +1280,17 @@ index 0000000..42c200e + + // make gpio-host type call to gpio + deb_verbose("Passthrough in host with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); -+ /* -+ #ifdef GPIO_DEBUG -+ deb_verbose("Debug abort\n"); -+ return 0; -+ #endif -+ */ + + switch (kbuf->signal) { + case GPIO_READL: + kbuf_rw = (struct tegra_readl_writel *)kbuf; + deb_verbose("readl debug A\n"); ++ if( !access_ok(kbuf_rw->address, sizeof(u32)) ) { ++ deb_info("cannot access address 0x%p", kbuf_rw->address); ++ ret = 0xDEADBEEF; ++ goto debug1; ++ } ++ goto debug1; + switch (kbuf_rw->rwltype) { + case RWL_STD: + ret = (int)readl(kbuf_rw->address); @@ -1313,12 +1302,18 @@ index 0000000..42c200e + ret = (int)readl_relaxed(kbuf_rw->address); + break; + } ++ debug1: + deb_verbose("readl debug B\n"); + goto retval; + break; + case GPIO_WRITEL: + kbuf_rw = (struct tegra_readl_writel *)kbuf; + deb_verbose("writel debug A\n"); ++ if( !access_ok(kbuf_rw->address, sizeof(u32)) ) { ++ deb_info("cannot access address 0x%p", kbuf_rw->address); ++ goto debug2; ++ } ++ goto debug2; + switch (kbuf_rw->rwltype) { + case RWL_STD: + writel(kbuf_rw->value, kbuf_rw->address); @@ -1329,6 +1324,7 @@ index 0000000..42c200e + case RWL_RELAXED: + writel_relaxed(kbuf_rw->value, kbuf_rw->address); + break; ++ debug2: + deb_verbose("writel debug B\n"); + goto end; + } @@ -1337,9 +1333,15 @@ index 0000000..42c200e + // if switch above is triggered we will either goto retval or goto end + + chip = find_chip_by_id(kbuf->chipnum); ++ if(!chip) { ++ pr_err("In GPIO_REQ, chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); ++ kfree(kbuf); ++ return -ENODEV; ++ } + + switch (kbuf->signal) { + case GPIO_REQ: ++ /* + #ifdef GPIO_DEBUG_VERBOSE + chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); + if(chip != chip_alt) { @@ -1347,11 +1349,7 @@ index 0000000..42c200e + chip = chip_alt; // we assume find_chip_by_name is more reliable + } + #endif -+ if(!chip) { -+ pr_err("In GPIO_REQ, chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); -+ kfree(kbuf); -+ return -ENODEV; -+ } ++ */ + deb_verbose("GPIO_REQ, using GPIO chip %s, for device %d\n", chip->label, kbuf->chipnum); + ret = chip->request(chip, kbuf->offset); + goto end; From 674d97b6e323607ffe241ccbd33a8648a8decba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Thu, 25 Jul 2024 10:53:14 +0000 Subject: [PATCH 23/26] write return value to char buffer in write() - return value written to char buffer in write() - return values from host seem reliable - return value is 64 bit written at offset 2 words, 2*8 bytes - full chain to guest does not work (qemu problem?) - platform to fix qemu problem - Guest kernel is not aware of pins in host (gpioinfo gives false data) - kernel: 5b7326f7ce046a8ae6c943e238a645356f237f7b/e625198f19ab6874ea89e0a7b1c5c4dfacb38710 - precompiled as nix generation 274 --- mk_0003.sh | 37 - .../patches/0003-gpio-virt-kernel.patch | 780 +++++++++-------- .../patches/0004-gpio-virt-drivers.patch | 811 ++++++++++-------- 3 files changed, 851 insertions(+), 777 deletions(-) delete mode 100755 mk_0003.sh diff --git a/mk_0003.sh b/mk_0003.sh deleted file mode 100755 index 1b5e6b326..000000000 --- a/mk_0003.sh +++ /dev/null @@ -1,37 +0,0 @@ -home="/home/$(id -un)" -# sw=${PWD} -sw="${home}/software" -tegra="${home}/software/Jetson/Linux_for_Tegra" -kern="${tegra}/sources/kernel" -ghaf="${sw}/ghaf" -#patchdir="${ghaf}/modules/hardware/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches" -patchdir="${ghaf}/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/" - - -# create empty tree to git diff against -empty=$(git hash-object -t tree /dev/null) - -# ------ - -pushd $kern - -# ------ -echo "this script updates only the 0003-gpio-virt-kernel.patch, before rebuilding Ghaf" - -# 0003-gpio-virt-kernel.patch # exclude /drive/Kconfig and drive/Makefile -git -C kernel-5.10/ diff basepoint -- drivers/gpio/ \ - >${patchdir}/0003-gpio-virt-kernel.patch -git -C kernel-5.10/ diff basepoint -- drivers/pinctrl/ \ - >>${patchdir}/0003-gpio-virt-kernel.patch -git -C kernel-5.10/ diff basepoint -- include/ \ - >>${patchdir}/0003-gpio-virt-kernel.patch - -# ------ - -# build ghaf -cd ${ghaf} -./build.sh - -# ------ - -popd diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch index 55323fc1e..34bec92ab 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch @@ -1,24 +1,28 @@ diff --git a/drivers/gpio/gpio-proxy.h b/drivers/gpio/gpio-proxy.h new file mode 100644 -index 000000000000..5cefb69d83ab +index 000000000000..723738267d3a --- /dev/null +++ b/drivers/gpio/gpio-proxy.h -@@ -0,0 +1,87 @@ +@@ -0,0 +1,92 @@ +#ifndef GPIO_PROXY_H +#define GPIO_PROXY_H + -+/* passthrough hooks for low level functions sudh as readl adnn writel ++/* passthrough hooks for low level functions such as readl and writel + * functions are mainly intended for GPIO passthrough + */ + +extern bool kernel_is_on_guest; +extern inline u32 readl_redirect( void * addr, unsigned char type); +extern inline void writel_redirect( u32 value, void * addr, unsigned char type); ++extern void __iomem *tegra186_gpio_get_base_redirect(unsigned char id, unsigned int pin); + +extern const unsigned char rwl_std_type; +extern const unsigned char rwl_raw_type; +extern const unsigned char rwl_relaxed_type; + ++// TODO ++// check readl_x() and writel_x() in files: gpio-tegra.c, pinctrl-tegra.c ++ +static inline u32 readl_x( void * addr) { + u32 ret; + if(kernel_is_on_guest) { @@ -57,8 +61,8 @@ index 000000000000..5cefb69d83ab + else { + __raw_writel(value, addr); + } -+ +}; ++ +static inline u32 readl_relaxed_x( void * addr) { + u32 ret; + if(kernel_is_on_guest) { @@ -81,14 +85,15 @@ index 000000000000..5cefb69d83ab + + +// TODO, adding these passthroughs would make execution less latent ++/* +static inline u32 pmx_readl_x( void * addr) { return 0; }; +static inline void pmx_writel_x( u32 value, void * addr) {}; +static inline u32 tegra_gpio_readl_x( void * addr) { return 0;}; +static inline void tegra_gpio_writel_x( u32 value, void * addr) {}; +static inline u32 tegra_gte_readl_x( void * addr) { return 0; }; +static inline void tegra_gte_writel_x( u32 value, void * addr) {}; -+ -+// note: adding even higher level functions migth take latency off the lower level functions ++*/ ++// note: adding more higher level functions migth take latency off the lower level functions + +#endif diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c @@ -346,7 +351,7 @@ index f66fc17faee4..9706a77fe5a5 100644 } subsys_initcall(tegra_gpio_init); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c -index 5e57824b283e..de08bdbf435a 100644 +index 5e57824b283e..2743beecfa2f 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -20,6 +20,46 @@ @@ -378,7 +383,7 @@ index 5e57824b283e..de08bdbf435a 100644 + + #include "gpiolib.h" + #include -+ #include "gpio-proxy.h" // low level hooks for readl and writel ++ #include "gpio-proxy.h" // low level inline hooks for readl and writel + + bool kernel_is_on_guest = false; + EXPORT_SYMBOL_GPL(kernel_is_on_guest); @@ -438,13 +443,11 @@ index 5e57824b283e..de08bdbf435a 100644 }; struct tegra_gpio { -@@ -278,13 +318,17 @@ static struct tegra_gte_info tegra194_gte_info[] = { +@@ -278,13 +318,13 @@ static struct tegra_gte_info tegra194_gte_info[] = { static inline u32 tegra_gte_readl(struct tegra_gpio *tgi, u32 reg) { - return __raw_readl(tgi->gte_regs + reg); -+ deb_verbose("\n"); -+ + return __raw_readl_x(tgi->gte_regs + reg); } @@ -452,13 +455,11 @@ index 5e57824b283e..de08bdbf435a 100644 u32 val) { - __raw_writel(val, tgi->gte_regs + reg); -+ deb_verbose("\n"); -+ + __raw_writel_x(val, tgi->gte_regs + reg); } static void tegra_gte_flush_fifo(struct tegra_gpio *tgi) -@@ -307,6 +351,8 @@ u64 tegra_gte_read_fifo(struct tegra_gpio *tgi, u32 offset) +@@ -307,6 +347,8 @@ u64 tegra_gte_read_fifo(struct tegra_gpio *tgi, u32 offset) u32 aon_bits; u32 bit_index = 0; @@ -467,7 +468,7 @@ index 5e57824b283e..de08bdbf435a 100644 /* Check if FIFO is empty */ while ((tegra_gte_readl(tgi, GTE_GPIO_TESTATUS) >> GTE_GPIO_TESTATUS_OCCUPANCY_SHIFT) & -@@ -348,6 +394,8 @@ int tegra_gte_enable_ts(struct tegra_gpio *tgi, u32 offset) +@@ -348,6 +390,8 @@ int tegra_gte_enable_ts(struct tegra_gpio *tgi, u32 offset) u32 val, mask, reg; int i = 0; @@ -476,7 +477,7 @@ index 5e57824b283e..de08bdbf435a 100644 if (tgi->gte_enable == 1) { dev_err(tgi->gpio.parent, "timestamp is already enabled for gpio\n"); return -EINVAL; -@@ -381,6 +429,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) +@@ -381,6 +425,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) { u32 val, mask; @@ -485,7 +486,7 @@ index 5e57824b283e..de08bdbf435a 100644 if (tgi->gte_enable == 0) { dev_err(tgi->gpio.parent, "timestamp is already disabled\n"); return 0; -@@ -405,6 +455,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) +@@ -405,6 +451,8 @@ int tegra_gte_disable_ts(struct tegra_gpio *tgi, u32 offset) int tegra_gte_setup(struct tegra_gpio *tgi) { @@ -494,7 +495,7 @@ index 5e57824b283e..de08bdbf435a 100644 tegra_gte_writel(tgi, GTE_GPIO_TECTRL, 0); tgi->gte_enable = 0; -@@ -418,6 +470,8 @@ tegra186_gpio_get_port(struct tegra_gpio *gpio, unsigned int *pin) +@@ -418,6 +466,8 @@ tegra186_gpio_get_port(struct tegra_gpio *gpio, unsigned int *pin) { unsigned int start = 0, i; @@ -503,7 +504,7 @@ index 5e57824b283e..de08bdbf435a 100644 for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; -@@ -438,6 +492,8 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, +@@ -438,6 +488,8 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, const struct tegra_gpio_port *port; unsigned int offset; @@ -512,7 +513,32 @@ index 5e57824b283e..de08bdbf435a 100644 port = tegra186_gpio_get_port(gpio, &pin); if (!port) return NULL; -@@ -453,6 +509,8 @@ static void __iomem *tegra186_gpio_get_secure(struct tegra_gpio *gpio, +@@ -447,12 +499,33 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, + return gpio->base + offset + pin * 0x20; + } + ++inline struct tegra_gpio * find_tegra_chip_by_id(int id); ++ ++// executes tegra186_gpio_get_base in host as a proxy for guest ++void __iomem *tegra186_gpio_get_base_execute(int id, unsigned int pin) ++ { ++ struct tegra_gpio *gpio = find_tegra_chip_by_id(id); ++ return tegra186_gpio_get_base(gpio, pin); ++ } ++EXPORT_SYMBOL_GPL(tegra186_gpio_get_base_execute); ++ ++static inline void __iomem *tegra186_gpio_get_base_x(struct tegra_gpio *tgpio, unsigned int pin) { ++ if(kernel_is_on_guest) { ++ return tegra186_gpio_get_base_redirect(tgpio->gpio.gpiodev->id, pin); ++ } ++ else { ++ return tegra186_gpio_get_base(tgpio, pin); ++ } ++}; ++ + static void __iomem *tegra186_gpio_get_secure(struct tegra_gpio *gpio, + unsigned int pin) + { const struct tegra_gpio_port *port; unsigned int offset; @@ -521,7 +547,7 @@ index 5e57824b283e..de08bdbf435a 100644 port = tegra186_gpio_get_port(gpio, &pin); if (!port) return NULL; -@@ -466,14 +524,17 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) +@@ -466,14 +539,17 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) void __iomem *secure; u32 val; @@ -532,7 +558,7 @@ index 5e57824b283e..de08bdbf435a 100644 - val = __raw_readl(secure + GPIO_VM_REG); - if ((val & GPIO_VM_RW) != GPIO_VM_RW) - return false; -+ val = __raw_readl_x(secure + GPIO_VM_REG); ++ val = __raw_readl_x(secure + GPIO_VM_REG); + if ((val & GPIO_VM_RW) != GPIO_VM_RW) + return false; } @@ -543,11 +569,12 @@ index 5e57824b283e..de08bdbf435a 100644 if ((val & (GPIO_SCR_SEC_ENABLE)) == 0) return true; -@@ -484,13 +545,15 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) +@@ -484,13 +560,16 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) return false; } -static int tegra186_gpio_get_direction(struct gpio_chip *chip, ++// function has passthrough version +int tegra186_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { @@ -560,12 +587,7 @@ index 5e57824b283e..de08bdbf435a 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -498,14 +561,15 @@ static int tegra186_gpio_get_direction(struct gpio_chip *chip, - if (WARN_ON(base == NULL)) - return -ENODEV; - -- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); -+ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); +@@ -502,10 +581,12 @@ static int tegra186_gpio_get_direction(struct gpio_chip *chip, if (value & TEGRA186_GPIO_ENABLE_CONFIG_OUT) return GPIO_LINE_DIRECTION_OUT; @@ -574,11 +596,12 @@ index 5e57824b283e..de08bdbf435a 100644 } -static int tegra186_gpio_direction_input(struct gpio_chip *chip, ++// function has passthrough version +int tegra186_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct tegra_gpio *gpio = gpiochip_get_data(chip); -@@ -513,6 +577,8 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, +@@ -513,6 +594,8 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, u32 value; int ret = 0; @@ -587,35 +610,17 @@ index 5e57824b283e..de08bdbf435a 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -520,14 +586,14 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, - if (WARN_ON(base == NULL)) - return -ENODEV; - -- value = readl(base + TEGRA186_GPIO_OUTPUT_CONTROL); -+ value = readl_x(base + TEGRA186_GPIO_OUTPUT_CONTROL); - value |= TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED; -- writel(value, base + TEGRA186_GPIO_OUTPUT_CONTROL); -+ writel_x(value, base + TEGRA186_GPIO_OUTPUT_CONTROL); - -- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); -+ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); - value |= TEGRA186_GPIO_ENABLE_CONFIG_ENABLE; - value &= ~TEGRA186_GPIO_ENABLE_CONFIG_OUT; -- writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); -+ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); - - ret = pinctrl_gpio_direction_input(chip->base + offset); - if (ret < 0) -@@ -536,7 +602,7 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, +@@ -536,7 +619,8 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, return ret; } -static int tegra186_gpio_direction_output(struct gpio_chip *chip, ++// function has passthrough version +int tegra186_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int level) { struct tegra_gpio *gpio = gpiochip_get_data(chip); -@@ -544,6 +610,8 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, +@@ -544,6 +628,8 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, u32 value; int ret = 0; @@ -624,26 +629,15 @@ index 5e57824b283e..de08bdbf435a 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -555,14 +623,14 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, - return -EINVAL; - - /* set the direction */ -- value = readl(base + TEGRA186_GPIO_OUTPUT_CONTROL); -+ value = readl_x(base + TEGRA186_GPIO_OUTPUT_CONTROL); - value &= ~TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED; -- writel(value, base + TEGRA186_GPIO_OUTPUT_CONTROL); -+ writel_x(value, base + TEGRA186_GPIO_OUTPUT_CONTROL); - -- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); -+ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); - value |= TEGRA186_GPIO_ENABLE_CONFIG_ENABLE; - value |= TEGRA186_GPIO_ENABLE_CONFIG_OUT; -- writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); -+ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); - ret = pinctrl_gpio_direction_output(chip->base + offset); +@@ -571,6 +657,7 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, + return ret; + } - if (ret < 0) -@@ -578,6 +646,8 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, ++// function has passthrough version + static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, + enum gpiod_flags dflags) + { +@@ -578,6 +665,8 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, struct tegra_gpio_saved_register *regs; void __iomem *base; @@ -652,20 +646,15 @@ index 5e57824b283e..de08bdbf435a 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -586,9 +656,9 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, - return -EINVAL; +@@ -598,6 +687,7 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, + return tegra186_gpio_direction_input(chip, offset); + } - regs = &gpio->gpio_rval[offset]; -- regs->conf = readl(base + TEGRA186_GPIO_ENABLE_CONFIG), -- regs->out = readl(base + TEGRA186_GPIO_OUTPUT_CONTROL), -- regs->val = readl(base + TEGRA186_GPIO_OUTPUT_VALUE), -+ regs->conf = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG), -+ regs->out = readl_x(base + TEGRA186_GPIO_OUTPUT_CONTROL), -+ regs->val = readl_x(base + TEGRA186_GPIO_OUTPUT_VALUE), - regs->restore_needed = true; - - if (dflags & GPIOD_FLAGS_BIT_DIR_OUT) -@@ -606,14 +676,16 @@ static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, ++// function has passthrough version + static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, + int enable) + { +@@ -606,6 +696,8 @@ static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, int value; int ret; @@ -674,17 +663,16 @@ index 5e57824b283e..de08bdbf435a 100644 base = tegra186_gpio_get_base(gpio, offset); if (WARN_ON(base == NULL)) return -EINVAL; - +@@ -613,7 +705,7 @@ static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, if (gpio->use_timestamp) { -- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); -+ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); + value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); value |= TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMP_FUNC; - writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); -+ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); ++ writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); if (enable) ret = tegra_gte_enable_ts(gpio, offset); else -@@ -630,6 +702,8 @@ static int tegra_gpio_timestamp_read(struct gpio_chip *chip, unsigned offset, +@@ -630,6 +722,8 @@ static int tegra_gpio_timestamp_read(struct gpio_chip *chip, unsigned offset, struct tegra_gpio *tgi = gpiochip_get_data(chip); int ret; @@ -693,7 +681,14 @@ index 5e57824b283e..de08bdbf435a 100644 if (tgi->use_timestamp) { *ts = tegra_gte_read_fifo(tgi, offset); ret = 0; -@@ -645,40 +719,54 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) +@@ -639,12 +733,15 @@ static int tegra_gpio_timestamp_read(struct gpio_chip *chip, unsigned offset, + return ret; + } + ++// function has passthrough version + static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) + { + struct tegra_gpio *gpio = gpiochip_get_data(chip); void __iomem *base; u32 value; @@ -702,20 +697,12 @@ index 5e57824b283e..de08bdbf435a 100644 base = tegra186_gpio_get_base(gpio, offset); if (WARN_ON(base == NULL)) return -ENODEV; - -- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); -+ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); - if (value & TEGRA186_GPIO_ENABLE_CONFIG_OUT) -- value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); -+ value = readl_x(base + TEGRA186_GPIO_OUTPUT_VALUE); - else -- value = readl(base + TEGRA186_GPIO_INPUT); -+ value = readl_x(base + TEGRA186_GPIO_INPUT); - +@@ -658,17 +755,20 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) return value & BIT(0); } -static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, ++// function has passthrough version +void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, int level) { @@ -734,17 +721,10 @@ index 5e57824b283e..de08bdbf435a 100644 if (WARN_ON(base == NULL)) return; -- value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); -+ value = readl_x(base + TEGRA186_GPIO_OUTPUT_VALUE); - if (level == 0) - value &= ~TEGRA186_GPIO_OUTPUT_VALUE_HIGH; - else - value |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH; +@@ -681,6 +781,17 @@ static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, + writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); + } -- writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); -+ writel_x(value, base + TEGRA186_GPIO_OUTPUT_VALUE); -+} -+ +void tegra186_gpio_set_by_name(const char *name, unsigned int offset, + int level) +{ @@ -753,10 +733,13 @@ index 5e57824b283e..de08bdbf435a 100644 + else { + pr_err("GPIO cannot find chip by name, %s\n", name); + } - } - ++} ++ ++// function has passthrough version static int tegra186_gpio_set_config(struct gpio_chip *chip, -@@ -689,6 +777,8 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, + unsigned int offset, + unsigned long config) +@@ -689,6 +800,8 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, u32 debounce, value; void __iomem *base; @@ -765,29 +748,15 @@ index 5e57824b283e..de08bdbf435a 100644 base = tegra186_gpio_get_base(gpio, offset); if (base == NULL) return -ENXIO; -@@ -708,15 +798,18 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, - debounce = DIV_ROUND_UP(debounce, USEC_PER_MSEC); - - value = TEGRA186_GPIO_DEBOUNCE_CONTROL_THRESHOLD(debounce); -- writel(value, base + TEGRA186_GPIO_DEBOUNCE_CONTROL); -+ writel_x(value, base + TEGRA186_GPIO_DEBOUNCE_CONTROL); - -- value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); -+ value = readl_x(base + TEGRA186_GPIO_ENABLE_CONFIG); - value |= TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE; -- writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); -+ writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); - +@@ -717,6 +830,7 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, return 0; } -+// TODO DEBUG FIXIT -+// for guest: -+// this function cannot maybe be redirected because we need to set up pointer 'chip' in guest! ++// passthrough but passthrough function is not used static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) { struct tegra_gpio *gpio = gpiochip_get_data(chip); -@@ -725,6 +818,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) +@@ -725,6 +839,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) unsigned int i, j; int err; @@ -796,7 +765,15 @@ index 5e57824b283e..de08bdbf435a 100644 if (!gpio->soc->pinmux || gpio->soc->num_pin_ranges == 0) return 0; -@@ -768,6 +863,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, +@@ -761,6 +877,7 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) + return 0; + } + ++// candidate for passthrough (but we still need irq in Guest) + static int tegra186_gpio_of_xlate(struct gpio_chip *chip, + const struct of_phandle_args *spec, + u32 *flags) +@@ -768,6 +885,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, struct tegra_gpio *gpio = gpiochip_get_data(chip); unsigned int port, pin, i, offset = 0; @@ -805,7 +782,19 @@ index 5e57824b283e..de08bdbf435a 100644 if (WARN_ON(chip->of_gpio_n_cells < 2)) return -EINVAL; -@@ -803,7 +900,7 @@ static void tegra186_irq_ack(struct irq_data *data) +@@ -793,19 +912,21 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, + + #define to_tegra_gpio(x) container_of((x), struct tegra_gpio, gpio) + ++// candidate for passthrough (but we still need irq in Guest) + static void tegra186_irq_ack(struct irq_data *data) + { + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + struct tegra_gpio *gpio = to_tegra_gpio(gc); + void __iomem *base; + +- base = tegra186_gpio_get_base(gpio, data->hwirq); ++ base = tegra186_gpio_get_base_x(gpio, data->hwirq); if (WARN_ON(base == NULL)) return; @@ -813,8 +802,16 @@ index 5e57824b283e..de08bdbf435a 100644 + writel_x(1, base + TEGRA186_GPIO_INTERRUPT_CLEAR); } ++// candidate for passthrough (but we still need irq in Guest) static void tegra186_irq_mask(struct irq_data *data) -@@ -817,9 +914,9 @@ static void tegra186_irq_mask(struct irq_data *data) + { + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); +@@ -813,15 +934,16 @@ static void tegra186_irq_mask(struct irq_data *data) + void __iomem *base; + u32 value; + +- base = tegra186_gpio_get_base(gpio, data->hwirq); ++ base = tegra186_gpio_get_base_x(gpio, data->hwirq); if (WARN_ON(base == NULL)) return; @@ -825,8 +822,16 @@ index 5e57824b283e..de08bdbf435a 100644 + writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); } ++// candidate for passthrough (but we still need irq in Guest) static void tegra186_irq_unmask(struct irq_data *data) -@@ -833,9 +930,9 @@ static void tegra186_irq_unmask(struct irq_data *data) + { + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); +@@ -829,15 +951,16 @@ static void tegra186_irq_unmask(struct irq_data *data) + void __iomem *base; + u32 value; + +- base = tegra186_gpio_get_base(gpio, data->hwirq); ++ base = tegra186_gpio_get_base_x(gpio, data->hwirq); if (WARN_ON(base == NULL)) return; @@ -837,8 +842,16 @@ index 5e57824b283e..de08bdbf435a 100644 + writel_x(value, base + TEGRA186_GPIO_ENABLE_CONFIG); } ++// candidate for passthrough (but we still need irq in Guest) static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) -@@ -849,7 +946,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) + { + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); +@@ -845,11 +968,11 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) + void __iomem *base; + u32 value; + +- base = tegra186_gpio_get_base(gpio, data->hwirq); ++ base = tegra186_gpio_get_base_x(gpio, data->hwirq); if (WARN_ON(base == NULL)) return -ENODEV; @@ -847,7 +860,7 @@ index 5e57824b283e..de08bdbf435a 100644 value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK; value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL; -@@ -883,7 +980,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) +@@ -883,7 +1006,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) return -EINVAL; } @@ -856,7 +869,15 @@ index 5e57824b283e..de08bdbf435a 100644 if ((type & IRQ_TYPE_EDGE_BOTH) == 0) irq_set_handler_locked(data, handle_level_irq); -@@ -930,7 +1027,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) +@@ -904,6 +1027,7 @@ static int tegra186_irq_set_wake(struct irq_data *data, unsigned int on) + return 0; + } + ++// candidate for passthrough (but we still need irq in Guest) + static void tegra186_gpio_irq(struct irq_desc *desc) + { + struct tegra_gpio *gpio = irq_desc_get_handler_data(desc); +@@ -930,7 +1054,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) if (j == gpio->num_irqs_per_bank) goto skip; @@ -865,7 +886,14 @@ index 5e57824b283e..de08bdbf435a 100644 for_each_set_bit(pin, &value, port->pins) { irq = irq_find_mapping(domain, offset + pin); -@@ -1037,6 +1134,8 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1031,12 +1155,15 @@ static const struct of_device_id tegra186_pmc_of_match[] = { + { /* sentinel */ } + }; + ++// readl and writel are called + static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) + { + struct device *dev = gpio->gpio.parent; unsigned int i, j; u32 value; @@ -874,7 +902,7 @@ index 5e57824b283e..de08bdbf435a 100644 for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; unsigned int offset, p = port->port; -@@ -1044,7 +1143,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1044,7 +1171,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) base = gpio->secure + port->bank * 0x1000 + 0x800; @@ -883,7 +911,7 @@ index 5e57824b283e..de08bdbf435a 100644 /* * For controllers that haven't been locked down yet, make -@@ -1058,7 +1157,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1058,7 +1185,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) */ for (j = 0; j < gpio->num_irqs_per_bank; j++) { dev_dbg(dev, "programming default interrupt routing for port %s\n", @@ -892,7 +920,7 @@ index 5e57824b283e..de08bdbf435a 100644 offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j); -@@ -1073,9 +1172,9 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1073,9 +1200,9 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) */ if (j == 0) { @@ -904,7 +932,7 @@ index 5e57824b283e..de08bdbf435a 100644 } } } -@@ -1107,6 +1206,211 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +@@ -1107,6 +1234,232 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) return -EINVAL; } @@ -943,7 +971,8 @@ index 5e57824b283e..de08bdbf435a 100644 + extern const struct tegra_gpio_port * + tegra186_gpio_get_port_redirect(struct tegra_gpio *gpio, unsigned int *pin); + -+ extern void __iomem *tegra186_gpio_get_base_redirect(struct tegra_gpio *gpio, ++ extern void __iomem *tegra186_gpio_get_base_redirect(unsigned char id, ++ + unsigned int pin); + + extern void __iomem *tegra186_gpio_get_secure_redirect(struct tegra_gpio *gpio, @@ -1050,6 +1079,7 @@ index 5e57824b283e..de08bdbf435a 100644 + static void preserve_tegrachip(struct tegra_gpio *tegrachip) { + struct gpio_chip *gpiochip = &(tegrachip->gpio); + static int gpio_chip_count = 0; ++ deb_debug("entering with gpio_chip_count = %d\n", gpio_chip_count); + + if (gpio_chip_count >= MAX_CHIP) { + pr_err("GPIO, *ERROR* maximum chip count is exceeded (%d)", gpio_chip_count); @@ -1091,6 +1121,25 @@ index 5e57824b283e..de08bdbf435a 100644 + * one doubtful assumption is that chip pointers are numbered by the driver + * in the same order preserve_tegrachip records them */ + inline struct gpio_chip * find_chip_by_id(int id) { ++ int i = 0, r; ++ while ( (r = atomic_read(&tegra_gpio_hosts_ready)) != MAX_CHIP) { ++ msleep(100); // Sleep briefly instead of looping infinitely. ++ if( i++ > 10 ) { ++ pr_err("GPIO tegra_gpio_host chip table setup error: id=%d, count=%d\n", id, r); ++ if(r == 1) return &tegra_gpio_hosts[0]->gpio; ++ else return NULL; ++ } ++ } ++ if(id & ~0x00000001) { ++ pr_err("GPIO, *ERROR* Illegal chip number (%d)", id); ++ return 0; ++ } ++ else ++ return &tegra_gpio_hosts[id]->gpio; ++ } ++ EXPORT_SYMBOL_GPL(find_chip_by_id); ++ ++ inline struct tegra_gpio * find_tegra_chip_by_id(int id) { + /* + int i = 0; + while (atomic_read(&tegra_gpio_hosts_ready) != MAX_CHIP) { @@ -1106,9 +1155,9 @@ index 5e57824b283e..de08bdbf435a 100644 + return 0; + } + else -+ return &tegra_gpio_hosts[id]->gpio; ++ return tegra_gpio_hosts[id]; + } -+ EXPORT_SYMBOL_GPL(find_chip_by_id); ++ EXPORT_SYMBOL_GPL(find_tegra_chip_by_id); +#endif + +extern int devm_gpiochip_add_data__redirect(struct device *dev, struct gpio_chip *gc, void *data); @@ -1116,7 +1165,7 @@ index 5e57824b283e..de08bdbf435a 100644 static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; -@@ -1120,17 +1424,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1120,17 +1473,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) int value; void __iomem *base; @@ -1132,9 +1181,9 @@ index 5e57824b283e..de08bdbf435a 100644 + } + + #if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) -+ -+ deb_debug("GPIO Proxy code\n"); ++ deb_debug("GPIO Proxy code\n"); ++ + // If virtual-pa node is defined, it means that we are using a GPIO proxy + err = of_property_read_u64(pdev->dev.of_node, "virtual-pa", &gpio_vpa); + if(!err) { @@ -1161,7 +1210,7 @@ index 5e57824b283e..de08bdbf435a 100644 /* count the number of banks in the controller */ for (i = 0; i < gpio->soc->num_ports; i++) -@@ -1139,11 +1466,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1139,11 +1515,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->num_banks++; @@ -1178,7 +1227,7 @@ index 5e57824b283e..de08bdbf435a 100644 sizeof(*gpio->gpio_rval), GFP_KERNEL); if (!gpio->gpio_rval) return -ENOMEM; -@@ -1154,9 +1485,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1154,9 +1534,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -EINVAL; } @@ -1191,7 +1240,7 @@ index 5e57824b283e..de08bdbf435a 100644 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gte"); if (!res) { dev_err(&pdev->dev, "Missing gte MEM resource\n"); -@@ -1172,14 +1506,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1172,14 +1555,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) } err = platform_irq_count(pdev); @@ -1215,7 +1264,7 @@ index 5e57824b283e..de08bdbf435a 100644 gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), GFP_KERNEL); -@@ -1188,27 +1528,43 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1188,27 +1577,43 @@ static int tegra186_gpio_probe(struct platform_device *pdev) for (i = 0; i < gpio->num_irq; i++) { err = platform_get_irq(pdev, i); @@ -1275,7 +1324,7 @@ index 5e57824b283e..de08bdbf435a 100644 for (i = 0; i < gpio->soc->num_ports; i++) gpio->gpio.ngpio += gpio->soc->ports[i].pins; -@@ -1229,6 +1585,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1229,6 +1634,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -ENOMEM; names[offset + j] = name; @@ -1283,32 +1332,35 @@ index 5e57824b283e..de08bdbf435a 100644 } offset += port->pins; -@@ -1260,6 +1617,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1260,6 +1666,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parent_handler_data = gpio; irq->num_parents = gpio->num_irq; -+//DEBUG -+deb_verbose("gpio->gpio.of_node = 0x%llx, pdev->dev.of_node = 0x%llx", (long long unsigned int)gpio->gpio.of_node, (long long unsigned int)pdev->dev.of_node); ++ //DEBUG ++ deb_verbose("gpio->gpio.of_node = 0x%llx, pdev->dev.of_node = 0x%llx", (long long unsigned int)gpio->gpio.of_node, (long long unsigned int)pdev->dev.of_node); /* * To simplify things, use a single interrupt per bank for now. Some -@@ -1283,8 +1642,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) - irq->parents = gpio->irq; +@@ -1284,7 +1692,18 @@ static int tegra186_gpio_probe(struct platform_device *pdev) } -+ #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) -+ // we cannot physically set up irq on guest -+ if(!kernel_is_on_guest) { -+ #endif if (gpio->soc->num_irqs_per_bank > 1) - tegra186_gpio_init_route_mapping(gpio); ++ /* + #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) -+ } -+ #endif ++ if(kernel_is_on_guest) { ++ tegra186_gpio_init_route_mapping_redirect(gpio); ++ } ++ else { ++ tegra186_gpio_init_route_mapping(gpio); ++ } ++ #else ++ */ + tegra186_gpio_init_route_mapping(gpio); ++ // #endif np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (!of_device_is_available(np)) -@@ -1315,9 +1681,36 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1315,9 +1734,31 @@ static int tegra186_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, gpio); @@ -1339,17 +1391,18 @@ index 5e57824b283e..de08bdbf435a 100644 + return err; + } + -+ // DEBUG -+ // #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) -+ // if(kernel_is_on_guest) goto guest_skip; -+ // #endif -+ + /* on guest, we could possibly passsthrough the whole loop below for better performance */ if (gpio->soc->is_hw_ts_sup) { for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) { -@@ -1329,24 +1722,25 @@ static int tegra186_gpio_probe(struct platform_device *pdev) - if (WARN_ON(base == NULL)) +@@ -1325,28 +1766,29 @@ static int tegra186_gpio_probe(struct platform_device *pdev) + &gpio->soc->ports[i]; + + for (j = 0; j < port->pins; j++) { +- base = tegra186_gpio_get_base(gpio, offset + j); +- if (WARN_ON(base == NULL)) ++ base = tegra186_gpio_get_base_x(gpio, offset + j); ++ if (WARN_ON(base == NULL)) // BUG here, base is null return -EINVAL; - value = readl(base + @@ -1367,11 +1420,10 @@ index 5e57824b283e..de08bdbf435a 100644 - if (gpio->use_timestamp) - tegra_gte_setup(gpio); -- -- return 0; + #if defined(CONFIG_TEGRA_GPIO_HOST_PROXY) || defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) + preserve_tegrachip(gpio); -+ // guest_skip: + +- return 0; + #endif + return 0; } @@ -1381,7 +1433,16 @@ index 5e57824b283e..de08bdbf435a 100644 static int tegra_gpio_resume_early(struct device *dev) { struct tegra_gpio *gpio = dev_get_drvdata(dev); -@@ -1366,9 +1760,9 @@ static int tegra_gpio_resume_early(struct device *dev) +@@ -1355,7 +1797,7 @@ static int tegra_gpio_resume_early(struct device *dev) + void __iomem *base; + int i; + +- base = tegra186_gpio_get_base(gpio, offset); ++ base = tegra186_gpio_get_base_x(gpio, offset); + if (WARN_ON(base == NULL)) + return -EINVAL; + +@@ -1366,9 +1808,9 @@ static int tegra_gpio_resume_early(struct device *dev) regs->restore_needed = false; @@ -1394,7 +1455,7 @@ index 5e57824b283e..de08bdbf435a 100644 } return 0; -@@ -1723,7 +2117,9 @@ static struct platform_driver tegra186_gpio_driver = { +@@ -1723,7 +2165,9 @@ static struct platform_driver tegra186_gpio_driver = { .probe = tegra186_gpio_probe, .remove = tegra186_gpio_remove, }; @@ -1719,15 +1780,15 @@ index 30e2476a6dc4..aab40f1cd52f 100644 err = gpio_request_one(array->gpio, array->flags, array->label); if (err) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c -index 647e77db82b1..bc7298aa06ce 100644 +index 647e77db82b1..4b85fcb8b62b 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c -@@ -24,6 +24,27 @@ +@@ -24,6 +24,26 @@ #include "gpiolib.h" #include "gpiolib-of.h" +#define GPIO_DEBUG -+#define GPIO_DEBUG_VERBOSE ++// #define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG + #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) @@ -1744,35 +1805,25 @@ index 647e77db82b1..bc7298aa06ce 100644 +#endif + +#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) -+#include "gpio-proxy.h" // low level hooks for readl_x and writel_x +#endif // CONFIG_TEGRA_GPIO_GUEST_PROXY and CONFIG_TEGRA_GPIO_HOST_PROXY + /** * of_gpio_spi_cs_get_count() - special GPIO counting for SPI * @dev: Consuming device -@@ -1061,16 +1082,21 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) - group_names = of_find_property(np, group_names_propname, NULL); - - for (;; index++) { -+ deb_verbose("index at A: %d", index); - ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, +@@ -1065,12 +1085,10 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) index, &pinspec); if (ret) break; - -+ deb_verbose("index at B: %d", index); -+ +- pctldev = of_pinctrl_get(pinspec.np); of_node_put(pinspec.np); if (!pctldev) return -EPROBE_DEFER; - -+ deb_verbose("index at C: %d", index); -+ +- if (pinspec.args[2]) { if (group_names) { of_property_read_string_index(np, -@@ -1141,7 +1167,6 @@ int of_gpiochip_add(struct gpio_chip *chip) +@@ -1141,7 +1159,6 @@ int of_gpiochip_add(struct gpio_chip *chip) chip->of_gpio_n_cells = 2; chip->of_xlate = of_gpio_simple_xlate; } @@ -1780,7 +1831,7 @@ index 647e77db82b1..bc7298aa06ce 100644 if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS) return -EINVAL; -@@ -1164,3 +1189,51 @@ void of_gpiochip_remove(struct gpio_chip *chip) +@@ -1164,3 +1181,39 @@ void of_gpiochip_remove(struct gpio_chip *chip) { of_node_put(chip->of_node); } @@ -1789,41 +1840,29 @@ index 647e77db82b1..bc7298aa06ce 100644 +{ + int ret; + -+deb_verbose("1.entry"); + if (!chip->of_node) + return 0; + -+deb_verbose("2"); + if (!chip->of_xlate) { + chip->of_gpio_n_cells = 2; + chip->of_xlate = of_gpio_simple_xlate; + } + -+deb_verbose("3"); + if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS) + return -EINVAL; + -+deb_verbose("4"); + of_gpiochip_init_valid_mask(chip); + -+deb_verbose("5"); + ret = of_gpiochip_add_pin_range(chip); -+ -+// TODO guest fails here: returns -517 -+ -+deb_verbose("of_gpiochip_add_pin_range returns: %d", ret); + if (ret) + return ret; + -+deb_verbose("6"); + of_node_get(chip->of_node); + -+deb_verbose("7"); +ret = of_gpiochip_scan_gpios(chip); + if (ret) + of_node_put(chip->of_node); + -+deb_verbose("8.return"); + return ret; +} + @@ -1863,15 +1902,15 @@ index 3ef71ca242ba..bf62aeee66a1 100644 /* If buf is not a number then try to find by name */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c -index 50abb1c20df0..e4648d29f26b 100644 +index 50abb1c20df0..cf7296db3190 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c -@@ -31,6 +31,27 @@ +@@ -31,6 +31,26 @@ #define CREATE_TRACE_POINTS #include +#define GPIO_DEBUG -+#define GPIO_DEBUG_VERBOSE ++// #define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG + #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) @@ -1888,13 +1927,12 @@ index 50abb1c20df0..e4648d29f26b 100644 +#endif + +#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) -+#include "gpio-proxy.h" // low level hooks for readl_x and writel_x +#endif // CONFIG_TEGRA_GPIO_GUEST_PROXY and CONFIG_TEGRA_GPIO_HOST_PROXY + /* Implementation infrastructure for GPIO interfaces. * * The GPIO programming interface allows for inlining speed-critical -@@ -45,11 +66,11 @@ +@@ -45,11 +65,11 @@ * * Otherwise, minimize overhead in what may be bitbanging codepaths. */ @@ -1908,7 +1946,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* Device and char device-related information */ static DEFINE_IDA(gpio_ida); -@@ -105,6 +126,8 @@ struct gpio_desc *gpio_to_desc(unsigned gpio) +@@ -105,6 +125,8 @@ struct gpio_desc *gpio_to_desc(unsigned gpio) { struct gpio_device *gdev; unsigned long flags; @@ -1917,7 +1955,7 @@ index 50abb1c20df0..e4648d29f26b 100644 spin_lock_irqsave(&gpio_lock, flags); -@@ -140,6 +163,8 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, +@@ -140,6 +162,8 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, { struct gpio_device *gdev = gc->gpiodev; @@ -1926,7 +1964,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (hwnum >= gdev->ngpio) return ERR_PTR(-EINVAL); -@@ -170,6 +195,8 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); +@@ -170,6 +194,8 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); */ struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) { @@ -1935,7 +1973,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!desc || !desc->gdev) return NULL; return desc->gdev->chip; -@@ -183,6 +210,7 @@ static int gpiochip_find_base(int ngpio) +@@ -183,6 +209,7 @@ static int gpiochip_find_base(int ngpio) int base = ARCH_NR_GPIOS - ngpio; list_for_each_entry_reverse(gdev, &gpio_devices, list) { @@ -1943,7 +1981,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* found a free space? */ if (gdev->base + gdev->ngpio <= base) break; -@@ -214,6 +242,8 @@ int gpiod_get_direction(struct gpio_desc *desc) +@@ -214,6 +241,8 @@ int gpiod_get_direction(struct gpio_desc *desc) unsigned offset; int ret; @@ -1952,7 +1990,7 @@ index 50abb1c20df0..e4648d29f26b 100644 gc = gpiod_to_chip(desc); offset = gpio_chip_hwgpio(desc); -@@ -253,9 +283,12 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) +@@ -253,6 +282,8 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) { struct gpio_device *prev, *next; @@ -1961,42 +1999,23 @@ index 50abb1c20df0..e4648d29f26b 100644 if (list_empty(&gpio_devices)) { /* initial entry in list */ list_add_tail(&gdev->list, &gpio_devices); -+deb_debug("debug 1"); - return 0; - } - -@@ -263,6 +296,7 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) - if (gdev->base + gdev->ngpio <= next->base) { - /* add before first entry */ - list_add(&gdev->list, &gpio_devices); -+deb_debug("debug 2"); - return 0; - } - -@@ -270,18 +304,21 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) +@@ -270,14 +301,12 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) if (prev->base + prev->ngpio <= gdev->base) { /* add behind last entry */ list_add_tail(&gdev->list, &gpio_devices); -+deb_debug("debug 3"); ++ deb_verbose("debug 3"); return 0; } list_for_each_entry_safe(prev, next, &gpio_devices, list) { /* at the end of the list */ - if (&next->list == &gpio_devices) -+{ deb_debug("debug 4, prev= 0x%p, next = 0x%p", prev, next); - break; +- if (&next->list == &gpio_devices) +- break; - -+} /* add between prev and next */ if (prev->base + prev->ngpio <= gdev->base && gdev->base + gdev->ngpio <= next->base) { - list_add(&gdev->list, &prev->list); -+deb_debug("debug 5"); - return 0; - } - } -@@ -301,6 +338,8 @@ struct gpio_desc *gpio_name_to_desc(const char * const name) +@@ -301,6 +330,8 @@ struct gpio_desc *gpio_name_to_desc(const char * const name) struct gpio_device *gdev; unsigned long flags; @@ -2005,7 +2024,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!name) return NULL; -@@ -340,6 +379,8 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) +@@ -340,6 +371,8 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) struct gpio_device *gdev = gc->gpiodev; int i; @@ -2014,7 +2033,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* First check all names if they are unique */ for (i = 0; i != gc->ngpio; ++i) { struct gpio_desc *gpio; -@@ -375,6 +416,8 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip) +@@ -375,6 +408,8 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip) int ret, i; int count; @@ -2023,7 +2042,7 @@ index 50abb1c20df0..e4648d29f26b 100644 count = fwnode_property_string_array_count(fwnode, "gpio-line-names"); if (count < 0) return 0; -@@ -409,6 +452,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) +@@ -409,6 +444,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) { unsigned long *p; @@ -2032,7 +2051,7 @@ index 50abb1c20df0..e4648d29f26b 100644 p = bitmap_alloc(gc->ngpio, GFP_KERNEL); if (!p) return NULL; -@@ -421,6 +466,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) +@@ -421,6 +458,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) { @@ -2041,7 +2060,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask)) return 0; -@@ -433,6 +480,8 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) +@@ -433,6 +472,8 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) static int gpiochip_init_valid_mask(struct gpio_chip *gc) { @@ -2050,7 +2069,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (gc->init_valid_mask) return gc->init_valid_mask(gc, gc->valid_mask, -@@ -443,12 +492,16 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) +@@ -443,12 +484,16 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_free_valid_mask(struct gpio_chip *gc) { @@ -2067,7 +2086,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (gc->add_pin_ranges) return gc->add_pin_ranges(gc); -@@ -458,6 +511,8 @@ static int gpiochip_add_pin_ranges(struct gpio_chip *gc) +@@ -458,6 +503,8 @@ static int gpiochip_add_pin_ranges(struct gpio_chip *gc) bool gpiochip_line_is_valid(const struct gpio_chip *gc, unsigned int offset) { @@ -2076,7 +2095,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* No mask means all valid */ if (likely(!gc->valid_mask)) return true; -@@ -470,6 +525,8 @@ static void gpiodevice_release(struct device *dev) +@@ -470,6 +517,8 @@ static void gpiodevice_release(struct device *dev) struct gpio_device *gdev = dev_get_drvdata(dev); unsigned long flags; @@ -2085,7 +2104,7 @@ index 50abb1c20df0..e4648d29f26b 100644 spin_lock_irqsave(&gpio_lock, flags); list_del(&gdev->list); spin_unlock_irqrestore(&gpio_lock, flags); -@@ -480,7 +537,7 @@ static void gpiodevice_release(struct device *dev) +@@ -480,7 +529,7 @@ static void gpiodevice_release(struct device *dev) kfree(gdev); } @@ -2094,7 +2113,7 @@ index 50abb1c20df0..e4648d29f26b 100644 #define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt)) #define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev)) #else -@@ -490,13 +547,66 @@ static void gpiodevice_release(struct device *dev) +@@ -490,13 +539,66 @@ static void gpiodevice_release(struct device *dev) */ #define gcdev_register(gdev, devt) device_add(&(gdev)->dev) #define gcdev_unregister(gdev) device_del(&(gdev)->dev) @@ -2162,7 +2181,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (ret) return ret; -@@ -504,6 +614,9 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) +@@ -504,6 +606,9 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) if (ret) goto err_remove_device; @@ -2172,7 +2191,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* From this point, the .release() function cleans up gpio_device */ gdev->dev.release = gpiodevice_release; pr_info("%s: registered GPIOs %d to %d on %s\n", -@@ -522,6 +635,8 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) +@@ -522,6 +627,8 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) struct gpio_desc *desc; int rv; @@ -2181,7 +2200,7 @@ index 50abb1c20df0..e4648d29f26b 100644 desc = gpiochip_get_desc(gc, hog->chip_hwnum); if (IS_ERR(desc)) { chip_err(gc, "%s: unable to get GPIO desc: %ld\n", __func__, -@@ -542,6 +657,8 @@ static void machine_gpiochip_add(struct gpio_chip *gc) +@@ -542,6 +649,8 @@ static void machine_gpiochip_add(struct gpio_chip *gc) { struct gpiod_hog *hog; @@ -2190,7 +2209,7 @@ index 50abb1c20df0..e4648d29f26b 100644 mutex_lock(&gpio_machine_hogs_mutex); list_for_each_entry(hog, &gpio_machine_hogs, list) { -@@ -557,6 +674,8 @@ static void gpiochip_setup_devs(void) +@@ -557,6 +666,8 @@ static void gpiochip_setup_devs(void) struct gpio_device *gdev; int ret; @@ -2199,7 +2218,7 @@ index 50abb1c20df0..e4648d29f26b 100644 list_for_each_entry(gdev, &gpio_devices, list) { ret = gpiochip_setup_dev(gdev); if (ret) -@@ -576,6 +695,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -576,6 +687,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = gc->base; struct gpio_device *gdev; @@ -2208,7 +2227,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* * First: allocate and populate the internal stat container, and * set up the struct device. -@@ -591,13 +712,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -591,13 +704,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gdev->dev.of_node = gc->parent->of_node; } @@ -2224,7 +2243,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* * Assign fwnode depending on the result of the previous calls, -@@ -689,9 +810,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -689,9 +802,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier); @@ -2236,7 +2255,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (gc->names) ret = gpiochip_set_desc_names(gc); -@@ -793,6 +914,277 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -793,6 +906,256 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, } EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); @@ -2361,18 +2380,11 @@ index 50abb1c20df0..e4648d29f26b 100644 + } + gdev->base = base; + ++ deb_verbose("precheck number of lines: %d, list=0x%llx, next=0x%llx, prev=0x%llx", gdev->ngpio, (long long unsigned int)&gdev->list, (long long unsigned int)gdev->list.next, (long long unsigned int)gdev->list.prev); + -+ -+//DEBUG -+ deb_verbose("precheck number of lines: %d, list=0x%llx, next=0x%llx, prev=0x%llx", gdev->ngpio, (long long unsigned int)&gdev->list, (long long unsigned int)gdev->list.next, (long long unsigned int)gdev->list.prev); -+ -+// debug gdev here --- empty list? + ret = gpiodev_add_to_list(gdev); -+ deb_verbose("number of lines: %d", gdev->ngpio); -+// XXX -+// TODO DEBUG -+// gpiodev_add_to_list returns empty list -+// Probably a BUG ++ if(WARN_ON(gdev->ngpio == 0)) ++ deb_verbose("number of lines: %d", gdev->ngpio); + if (ret) { + spin_unlock_irqrestore(&gpio_lock, flags); + goto err_free_label; @@ -2404,14 +2416,12 @@ index 50abb1c20df0..e4648d29f26b 100644 + /* TODO guest driver fails on of_gpiochip_add */ + ret = of_gpiochip_add__redirect(gc); + if (ret) -+// DEBUG +{ deb_verbose("of_gpiochip_add__redirect fails, with: %d", ret); + goto err_free_gpiochip_mask; +} + + ret = gpiochip_init_valid_mask(gc); + if (ret) -+// DEBUG +{ deb_verbose("gpiochip_init_valid_mask fails, with: %d", ret); + goto err_remove_of_chip; +} @@ -2430,7 +2440,6 @@ index 50abb1c20df0..e4648d29f26b 100644 + + ret = gpiochip_add_pin_ranges(gc); + if (ret) -+// DEBUG +{ deb_verbose("gpiochip_add_pin_ranges, with: %d", ret); + goto err_remove_of_chip; +} @@ -2467,41 +2476,30 @@ index 50abb1c20df0..e4648d29f26b 100644 + return 0; + +err_remove_irqchip: -+deb_debug("A"); + gpiochip_irqchip_remove(gc); +err_remove_irqchip_mask: -+deb_debug("B"); + gpiochip_irqchip_free_valid_mask(gc); +err_remove_acpi_chip: -+deb_debug("C"); + acpi_gpiochip_remove(gc); +err_remove_of_chip: -+deb_debug("D"); + gpiochip_free_hogs(gc); + of_gpiochip_remove(gc); +err_free_gpiochip_mask: -+deb_debug("E"); + gpiochip_remove_pin_ranges(gc); + gpiochip_free_valid_mask(gc); +err_remove_from_list: -+deb_debug("F"); + spin_lock_irqsave(&gpio_lock, flags); + list_del(&gdev->list); + spin_unlock_irqrestore(&gpio_lock, flags); +err_free_label: -+deb_debug("G"); + kfree_const(gdev->label); +err_free_descs: -+deb_debug("H"); + kfree(gdev->descs); +err_free_dev_name: -+deb_debug("I"); + kfree(dev_name(&gdev->dev)); +err_free_ida: -+deb_debug("J"); + ida_free(&gpio_ida, gdev->id); +err_free_gdev: -+deb_debug("K"); + /* failures here can mean systems won't boot... */ + pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__, + gdev->base, gdev->base + gdev->ngpio - 1, @@ -2514,7 +2512,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /** * gpiochip_get_data() - get per-subdriver data for the chip * @gc: GPIO chip -@@ -802,6 +1194,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); +@@ -802,6 +1165,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); */ void *gpiochip_get_data(struct gpio_chip *gc) { @@ -2523,7 +2521,7 @@ index 50abb1c20df0..e4648d29f26b 100644 return gc->gpiodev->data; } EXPORT_SYMBOL_GPL(gpiochip_get_data); -@@ -818,6 +1212,8 @@ void gpiochip_remove(struct gpio_chip *gc) +@@ -818,6 +1183,8 @@ void gpiochip_remove(struct gpio_chip *gc) unsigned long flags; unsigned int i; @@ -2532,7 +2530,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ gpiochip_sysfs_unregister(gdev); gpiochip_free_hogs(gc); -@@ -875,10 +1271,13 @@ struct gpio_chip *gpiochip_find(void *data, +@@ -875,10 +1242,13 @@ struct gpio_chip *gpiochip_find(void *data, struct gpio_chip *gc = NULL; unsigned long flags; @@ -2546,7 +2544,7 @@ index 50abb1c20df0..e4648d29f26b 100644 break; } -@@ -895,12 +1294,15 @@ static int gpiochip_match_name(struct gpio_chip *gc, void *data) +@@ -895,12 +1265,15 @@ static int gpiochip_match_name(struct gpio_chip *gc, void *data) return !strcmp(gc->label, name); } @@ -2564,7 +2562,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* * The following is irqchip helper code for gpiochips. -@@ -910,6 +1312,8 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc) +@@ -910,6 +1283,8 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc) { struct gpio_irq_chip *girq = &gc->irq; @@ -2573,7 +2571,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!girq->init_hw) return 0; -@@ -920,6 +1324,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -920,6 +1295,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) { struct gpio_irq_chip *girq = &gc->irq; @@ -2582,7 +2580,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!girq->init_valid_mask) return 0; -@@ -934,6 +1340,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -934,6 +1311,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) { @@ -2591,7 +2589,7 @@ index 50abb1c20df0..e4648d29f26b 100644 bitmap_free(gc->irq.valid_mask); gc->irq.valid_mask = NULL; } -@@ -941,6 +1349,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) +@@ -941,6 +1320,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, unsigned int offset) { @@ -2600,7 +2598,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!gpiochip_line_is_valid(gc, offset)) return false; /* No mask means all valid */ -@@ -966,6 +1376,8 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, +@@ -966,6 +1347,8 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, struct gpio_irq_chip *girq = &gc->irq; struct device *dev = &gc->gpiodev->dev; @@ -2609,7 +2607,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!girq->domain) { chip_err(gc, "called %s before setting up irqchip\n", __func__); -@@ -1011,7 +1423,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gc, +@@ -1011,7 +1394,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gc, } EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); @@ -2618,7 +2616,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /** * gpiochip_set_hierarchical_irqchip() - connects a hierarchical irqchip -@@ -1250,6 +1662,8 @@ void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, +@@ -1250,6 +1633,8 @@ void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, { struct irq_fwspec *fwspec; @@ -2627,7 +2625,7 @@ index 50abb1c20df0..e4648d29f26b 100644 fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); if (!fwspec) return NULL; -@@ -1269,6 +1683,8 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, +@@ -1269,6 +1654,8 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, { struct irq_fwspec *fwspec; @@ -2636,7 +2634,7 @@ index 50abb1c20df0..e4648d29f26b 100644 fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); if (!fwspec) return NULL; -@@ -1296,7 +1712,7 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) +@@ -1296,7 +1683,7 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) return false; } @@ -2645,7 +2643,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /** * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip -@@ -1314,6 +1730,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, +@@ -1314,6 +1701,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, struct gpio_chip *gc = d->host_data; int ret = 0; @@ -2654,7 +2652,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!gpiochip_irqchip_irq_valid(gc, hwirq)) return -ENXIO; -@@ -1415,7 +1833,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) +@@ -1415,7 +1804,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) if (!gpiochip_irqchip_irq_valid(gc, offset)) return -ENXIO; @@ -2663,7 +2661,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (irq_domain_is_hierarchy(domain)) { struct irq_fwspec spec; -@@ -1426,7 +1844,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) +@@ -1426,7 +1815,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) return irq_create_fwspec_mapping(&spec); } @@ -2672,7 +2670,7 @@ index 50abb1c20df0..e4648d29f26b 100644 return irq_create_mapping(domain, offset); } -@@ -1709,7 +2127,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, +@@ -1709,7 +2098,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, } gc->irq.threaded = threaded; of_node = gc->parent->of_node; @@ -2681,7 +2679,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* * If the gpiochip has an assigned OF node this takes precedence * FIXME: get rid of this and use gc->parent->of_node -@@ -1717,7 +2135,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, +@@ -1717,7 +2106,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, */ if (gc->of_node) of_node = gc->of_node; @@ -2690,7 +2688,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* * Specifying a default trigger is a terrible idea if DT or ACPI is * used to configure the interrupts, as you may end-up with -@@ -1796,7 +2214,7 @@ static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -1796,7 +2185,7 @@ static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) { } @@ -2699,7 +2697,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /** * gpiochip_generic_request() - request the gpio function for a pin -@@ -1805,10 +2223,12 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) +@@ -1805,10 +2194,12 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) */ int gpiochip_generic_request(struct gpio_chip *gc, unsigned offset) { @@ -2714,7 +2712,7 @@ index 50abb1c20df0..e4648d29f26b 100644 return pinctrl_gpio_request(gc->gpiodev->base + offset); } -@@ -1821,10 +2241,12 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); +@@ -1821,10 +2212,12 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); */ void gpiochip_generic_free(struct gpio_chip *gc, unsigned offset) { @@ -2729,7 +2727,7 @@ index 50abb1c20df0..e4648d29f26b 100644 pinctrl_gpio_free(gc->gpiodev->base + offset); } -@@ -1839,11 +2261,13 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free); +@@ -1839,11 +2232,13 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free); int gpiochip_generic_config(struct gpio_chip *gc, unsigned offset, unsigned long config) { @@ -2744,7 +2742,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /** * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping -@@ -1865,6 +2289,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, +@@ -1865,6 +2260,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, struct gpio_device *gdev = gc->gpiodev; int ret; @@ -2753,7 +2751,7 @@ index 50abb1c20df0..e4648d29f26b 100644 pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); if (!pin_range) { chip_err(gc, "failed to allocate pin ranges\n"); -@@ -1923,6 +2349,8 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, +@@ -1923,6 +2320,8 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, struct gpio_device *gdev = gc->gpiodev; int ret; @@ -2762,7 +2760,7 @@ index 50abb1c20df0..e4648d29f26b 100644 pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); if (!pin_range) { chip_err(gc, "failed to allocate pin ranges\n"); -@@ -1964,6 +2392,8 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) +@@ -1964,6 +2363,8 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) struct gpio_pin_range *pin_range, *tmp; struct gpio_device *gdev = gc->gpiodev; @@ -2771,7 +2769,7 @@ index 50abb1c20df0..e4648d29f26b 100644 list_for_each_entry_safe(pin_range, tmp, &gdev->pin_ranges, node) { list_del(&pin_range->node); pinctrl_remove_gpio_range(pin_range->pctldev, -@@ -1973,7 +2403,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) +@@ -1973,7 +2374,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) } EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); @@ -2780,7 +2778,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* These "optional" allocation calls help prevent drivers from stomping * on each other, and help provide better diagnostics in debugfs. -@@ -1987,6 +2417,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) +@@ -1987,6 +2388,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) bool hogged = false; unsigned offset; @@ -2789,7 +2787,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (label) { /* Free desc->label if already allocated. */ if (desc->label) { -@@ -2092,6 +2524,8 @@ int gpiod_request(struct gpio_desc *desc, const char *label) +@@ -2092,6 +2495,8 @@ int gpiod_request(struct gpio_desc *desc, const char *label) int ret = -EPROBE_DEFER; struct gpio_device *gdev; @@ -2798,7 +2796,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); gdev = desc->gdev; -@@ -2108,6 +2542,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label) +@@ -2108,6 +2513,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label) return ret; } @@ -2806,7 +2804,7 @@ index 50abb1c20df0..e4648d29f26b 100644 static bool gpiod_free_commit(struct gpio_desc *desc) { -@@ -2115,6 +2550,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2115,6 +2521,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) unsigned long flags; struct gpio_chip *gc; @@ -2815,7 +2813,7 @@ index 50abb1c20df0..e4648d29f26b 100644 might_sleep(); gpiod_unexport(desc); -@@ -2141,12 +2578,12 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2141,12 +2549,12 @@ static bool gpiod_free_commit(struct gpio_desc *desc) clear_bit(FLAG_EDGE_RISING, &desc->flags); clear_bit(FLAG_EDGE_FALLING, &desc->flags); clear_bit(FLAG_IS_HOGGED, &desc->flags); @@ -2832,7 +2830,7 @@ index 50abb1c20df0..e4648d29f26b 100644 ret = true; } -@@ -2159,6 +2596,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2159,6 +2567,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) void gpiod_free(struct gpio_desc *desc) { @@ -2841,7 +2839,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (desc && desc->gdev && gpiod_free_commit(desc)) { module_put(desc->gdev->owner); put_device(&desc->gdev->dev); -@@ -2166,6 +2605,7 @@ void gpiod_free(struct gpio_desc *desc) +@@ -2166,6 +2576,7 @@ void gpiod_free(struct gpio_desc *desc) WARN_ON(extra_checks); } } @@ -2849,7 +2847,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /** * gpiochip_is_requested - return string iff signal was requested -@@ -2184,6 +2624,8 @@ const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned offset) +@@ -2184,6 +2595,8 @@ const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned offset) { struct gpio_desc *desc; @@ -2858,7 +2856,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (offset >= gc->ngpio) return NULL; -@@ -2227,6 +2669,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, +@@ -2227,6 +2640,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, struct gpio_desc *desc = gpiochip_get_desc(gc, hwnum); int ret; @@ -2867,7 +2865,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (IS_ERR(desc)) { chip_err(gc, "failed to get GPIO descriptor\n"); return desc; -@@ -2274,6 +2718,8 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); +@@ -2274,6 +2689,8 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); static int gpio_do_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config) { @@ -2876,7 +2874,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!gc->set_config) return -ENOTSUPP; -@@ -2286,6 +2732,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) +@@ -2286,6 +2703,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) unsigned long config; unsigned arg; @@ -2885,7 +2883,7 @@ index 50abb1c20df0..e4648d29f26b 100644 switch (mode) { case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_UP: -@@ -2305,6 +2753,8 @@ static int gpio_set_bias(struct gpio_desc *desc) +@@ -2305,6 +2724,8 @@ static int gpio_set_bias(struct gpio_desc *desc) int bias = 0; int ret = 0; @@ -2894,7 +2892,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) bias = PIN_CONFIG_BIAS_DISABLE; else if (test_bit(FLAG_PULL_UP, &desc->flags)) -@@ -2334,6 +2784,8 @@ int gpiod_direction_input(struct gpio_desc *desc) +@@ -2334,6 +2755,8 @@ int gpiod_direction_input(struct gpio_desc *desc) struct gpio_chip *gc; int ret = 0; @@ -2903,7 +2901,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); gc = desc->gdev->chip; -@@ -2381,6 +2833,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) +@@ -2381,6 +2804,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) int val = !!value; int ret = 0; @@ -2912,7 +2910,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* * It's OK not to specify .direction_output() if the gpiochip is * output-only, but if there is then not even a .set() operation it -@@ -2431,6 +2885,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) +@@ -2431,6 +2856,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) */ int gpiod_direction_output_raw(struct gpio_desc *desc, int value) { @@ -2921,7 +2919,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); return gpiod_direction_output_raw_commit(desc, value); } -@@ -2452,6 +2908,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) +@@ -2452,6 +2879,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) { int ret; @@ -2930,7 +2928,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; -@@ -2523,6 +2981,8 @@ int gpiod_timestamp_control(struct gpio_desc *desc, int enable) +@@ -2523,6 +2952,8 @@ int gpiod_timestamp_control(struct gpio_desc *desc, int enable) { struct gpio_chip *chip; @@ -2939,7 +2937,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); chip = desc->gdev->chip; if (!chip->timestamp_control) { -@@ -2550,6 +3010,8 @@ int gpiod_timestamp_read(struct gpio_desc *desc, u64 *ts) +@@ -2550,6 +2981,8 @@ int gpiod_timestamp_read(struct gpio_desc *desc, u64 *ts) u64 gpio_ts; int ret; @@ -2948,7 +2946,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); chip = desc->gdev->chip; if (!chip->timestamp_read) { -@@ -2578,6 +3040,8 @@ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) +@@ -2578,6 +3011,8 @@ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) { struct gpio_chip *gc; @@ -2957,7 +2955,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); gc = desc->gdev->chip; -@@ -2598,6 +3062,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) +@@ -2598,6 +3033,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) { unsigned long config; @@ -2966,7 +2964,7 @@ index 50abb1c20df0..e4648d29f26b 100644 config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); return gpiod_set_config(desc, config); } -@@ -2618,6 +3084,8 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) +@@ -2618,6 +3055,8 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) int gpio; int rc; @@ -2975,7 +2973,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); /* * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for -@@ -2652,6 +3120,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_transitory); +@@ -2652,6 +3091,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_transitory); */ int gpiod_is_active_low(const struct gpio_desc *desc) { @@ -2984,7 +2982,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); return test_bit(FLAG_ACTIVE_LOW, &desc->flags); } -@@ -2663,6 +3133,8 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low); +@@ -2663,6 +3104,8 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low); */ void gpiod_toggle_active_low(struct gpio_desc *desc) { @@ -2993,7 +2991,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC_VOID(desc); change_bit(FLAG_ACTIVE_LOW, &desc->flags); } -@@ -2696,6 +3168,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) +@@ -2696,6 +3139,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) int offset; int value; @@ -3002,7 +3000,7 @@ index 50abb1c20df0..e4648d29f26b 100644 gc = desc->gdev->chip; offset = gpio_chip_hwgpio(desc); value = gc->get ? gc->get(gc, offset) : -EIO; -@@ -2707,6 +3181,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) +@@ -2707,6 +3152,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) static int gpio_chip_get_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { @@ -3011,7 +3009,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (gc->get_multiple) { return gc->get_multiple(gc, mask, bits); } else if (gc->get) { -@@ -2731,6 +3207,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, +@@ -2731,6 +3178,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, { int ret, i = 0; @@ -3020,7 +3018,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* * Validate array_info against desc_array and its size. * It should immediately follow desc_array if both -@@ -2837,6 +3315,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, +@@ -2837,6 +3286,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, */ int gpiod_get_raw_value(const struct gpio_desc *desc) { @@ -3029,7 +3027,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); /* Should be using gpiod_get_raw_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -2858,6 +3338,8 @@ int gpiod_get_value(const struct gpio_desc *desc) +@@ -2858,6 +3309,8 @@ int gpiod_get_value(const struct gpio_desc *desc) { int value; @@ -3038,7 +3036,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); /* Should be using gpiod_get_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -2892,6 +3374,8 @@ int gpiod_get_raw_array_value(unsigned int array_size, +@@ -2892,6 +3345,8 @@ int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3047,7 +3045,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(true, false, array_size, -@@ -2918,6 +3402,8 @@ int gpiod_get_array_value(unsigned int array_size, +@@ -2918,6 +3373,8 @@ int gpiod_get_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3056,7 +3054,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(false, false, array_size, -@@ -2937,6 +3423,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) +@@ -2937,6 +3394,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) struct gpio_chip *gc = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); @@ -3065,7 +3063,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (value) { ret = gc->direction_input(gc, offset); } else { -@@ -2962,6 +3450,8 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value +@@ -2962,6 +3421,8 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value struct gpio_chip *gc = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); @@ -3074,7 +3072,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (value) { ret = gc->direction_output(gc, offset, 1); if (!ret) -@@ -2980,6 +3470,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) +@@ -2980,6 +3441,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) { struct gpio_chip *gc; @@ -3083,7 +3081,7 @@ index 50abb1c20df0..e4648d29f26b 100644 gc = desc->gdev->chip; trace_gpio_value(desc_to_gpio(desc), 0, value); gc->set(gc, gpio_chip_hwgpio(desc), value); -@@ -2998,6 +3490,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) +@@ -2998,6 +3461,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) static void gpio_chip_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { @@ -3092,7 +3090,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (gc->set_multiple) { gc->set_multiple(gc, mask, bits); } else { -@@ -3017,6 +3511,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, +@@ -3017,6 +3482,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, { int i = 0; @@ -3101,7 +3099,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* * Validate array_info against desc_array and its size. * It should immediately follow desc_array if both -@@ -3122,6 +3618,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, +@@ -3122,6 +3589,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, */ void gpiod_set_raw_value(struct gpio_desc *desc, int value) { @@ -3110,7 +3108,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_raw_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -3140,6 +3638,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); +@@ -3140,6 +3609,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); */ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) { @@ -3119,7 +3117,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) -@@ -3163,6 +3663,8 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) +@@ -3163,6 +3634,8 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) */ void gpiod_set_value(struct gpio_desc *desc, int value) { @@ -3128,7 +3126,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -3188,6 +3690,8 @@ int gpiod_set_raw_array_value(unsigned int array_size, +@@ -3188,6 +3661,8 @@ int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3137,7 +3135,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(true, false, array_size, -@@ -3213,6 +3717,8 @@ int gpiod_set_array_value(unsigned int array_size, +@@ -3213,6 +3688,8 @@ int gpiod_set_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3146,7 +3144,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(false, false, array_size, -@@ -3228,6 +3734,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); +@@ -3228,6 +3705,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); */ int gpiod_cansleep(const struct gpio_desc *desc) { @@ -3155,7 +3153,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); return desc->gdev->chip->can_sleep; } -@@ -3240,6 +3748,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); +@@ -3240,6 +3719,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); */ int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name) { @@ -3164,7 +3162,7 @@ index 50abb1c20df0..e4648d29f26b 100644 VALIDATE_DESC(desc); if (name) { name = kstrdup_const(name, GFP_KERNEL); -@@ -3266,6 +3776,8 @@ int gpiod_to_irq(const struct gpio_desc *desc) +@@ -3266,6 +3747,8 @@ int gpiod_to_irq(const struct gpio_desc *desc) struct gpio_chip *gc; int offset; @@ -3173,7 +3171,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* * Cannot VALIDATE_DESC() here as gpiod_to_irq() consumer semantics * requires this function to not return zero on an invalid descriptor -@@ -3300,6 +3812,8 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq); +@@ -3300,6 +3783,8 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq); int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc; @@ -3182,7 +3180,7 @@ index 50abb1c20df0..e4648d29f26b 100644 desc = gpiochip_get_desc(gc, offset); if (IS_ERR(desc)) -@@ -3355,6 +3869,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3355,6 +3840,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc; @@ -3191,7 +3189,7 @@ index 50abb1c20df0..e4648d29f26b 100644 desc = gpiochip_get_desc(gc, offset); if (IS_ERR(desc)) return; -@@ -3372,6 +3888,8 @@ void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3372,6 +3859,8 @@ void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc = gpiochip_get_desc(gc, offset); @@ -3200,7 +3198,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!IS_ERR(desc) && !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); -@@ -3382,6 +3900,8 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3382,6 +3871,8 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc = gpiochip_get_desc(gc, offset); @@ -3209,7 +3207,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!IS_ERR(desc) && !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) { /* -@@ -3397,6 +3917,8 @@ EXPORT_SYMBOL_GPL(gpiochip_enable_irq); +@@ -3397,6 +3888,8 @@ EXPORT_SYMBOL_GPL(gpiochip_enable_irq); bool gpiochip_line_is_irq(struct gpio_chip *gc, unsigned int offset) { @@ -3218,7 +3216,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (offset >= gc->ngpio) return false; -@@ -3430,6 +3952,8 @@ EXPORT_SYMBOL_GPL(gpiochip_relres_irq); +@@ -3430,6 +3923,8 @@ EXPORT_SYMBOL_GPL(gpiochip_relres_irq); bool gpiochip_line_is_open_drain(struct gpio_chip *gc, unsigned int offset) { @@ -3227,7 +3225,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (offset >= gc->ngpio) return false; -@@ -3439,6 +3963,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); +@@ -3439,6 +3934,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); bool gpiochip_line_is_open_source(struct gpio_chip *gc, unsigned int offset) { @@ -3236,7 +3234,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (offset >= gc->ngpio) return false; -@@ -3448,6 +3974,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); +@@ -3448,6 +3945,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); bool gpiochip_line_is_persistent(struct gpio_chip *gc, unsigned int offset) { @@ -3245,7 +3243,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (offset >= gc->ngpio) return false; -@@ -3466,6 +3994,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); +@@ -3466,6 +3965,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); */ int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) { @@ -3254,7 +3252,7 @@ index 50abb1c20df0..e4648d29f26b 100644 might_sleep_if(extra_checks); VALIDATE_DESC(desc); return gpiod_get_raw_value_commit(desc); -@@ -3485,6 +4015,8 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) +@@ -3485,6 +3986,8 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) { int value; @@ -3263,7 +3261,7 @@ index 50abb1c20df0..e4648d29f26b 100644 might_sleep_if(extra_checks); VALIDATE_DESC(desc); value = gpiod_get_raw_value_commit(desc); -@@ -3516,6 +4048,8 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, +@@ -3516,6 +4019,8 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3272,7 +3270,7 @@ index 50abb1c20df0..e4648d29f26b 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3542,6 +4076,8 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, +@@ -3542,6 +4047,8 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3281,7 +3279,7 @@ index 50abb1c20df0..e4648d29f26b 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3563,6 +4099,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); +@@ -3563,6 +4070,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); */ void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { @@ -3290,7 +3288,7 @@ index 50abb1c20df0..e4648d29f26b 100644 might_sleep_if(extra_checks); VALIDATE_DESC_VOID(desc); gpiod_set_raw_value_commit(desc, value); -@@ -3581,6 +4119,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); +@@ -3581,6 +4090,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); */ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { @@ -3299,7 +3297,7 @@ index 50abb1c20df0..e4648d29f26b 100644 might_sleep_if(extra_checks); VALIDATE_DESC_VOID(desc); gpiod_set_value_nocheck(desc, value); -@@ -3604,6 +4144,8 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, +@@ -3604,6 +4115,8 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3308,7 +3306,7 @@ index 50abb1c20df0..e4648d29f26b 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3621,6 +4163,8 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) +@@ -3621,6 +4134,8 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) { unsigned int i; @@ -3317,7 +3315,7 @@ index 50abb1c20df0..e4648d29f26b 100644 mutex_lock(&gpio_lookup_lock); for (i = 0; i < n; i++) -@@ -3646,6 +4190,8 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, +@@ -3646,6 +4161,8 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3326,7 +3324,7 @@ index 50abb1c20df0..e4648d29f26b 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3661,6 +4207,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); +@@ -3661,6 +4178,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); */ void gpiod_add_lookup_table(struct gpiod_lookup_table *table) { @@ -3335,7 +3333,7 @@ index 50abb1c20df0..e4648d29f26b 100644 mutex_lock(&gpio_lookup_lock); list_add_tail(&table->list, &gpio_lookup_list); -@@ -3675,6 +4223,8 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); +@@ -3675,6 +4194,8 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); */ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) { @@ -3344,7 +3342,7 @@ index 50abb1c20df0..e4648d29f26b 100644 mutex_lock(&gpio_lookup_lock); list_del(&table->list); -@@ -3692,6 +4242,8 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) +@@ -3692,6 +4213,8 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) struct gpio_chip *gc; struct gpiod_hog *hog; @@ -3353,7 +3351,7 @@ index 50abb1c20df0..e4648d29f26b 100644 mutex_lock(&gpio_machine_hogs_mutex); for (hog = &hogs[0]; hog->chip_label; hog++) { -@@ -3715,6 +4267,8 @@ static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) +@@ -3715,6 +4238,8 @@ static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) const char *dev_id = dev ? dev_name(dev) : NULL; struct gpiod_lookup_table *table; @@ -3362,7 +3360,7 @@ index 50abb1c20df0..e4648d29f26b 100644 mutex_lock(&gpio_lookup_lock); list_for_each_entry(table, &gpio_lookup_list, list) { -@@ -3748,6 +4302,8 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, +@@ -3748,6 +4273,8 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, struct gpiod_lookup_table *table; struct gpiod_lookup *p; @@ -3371,7 +3369,7 @@ index 50abb1c20df0..e4648d29f26b 100644 table = gpiod_find_lookup_table(dev); if (!table) return desc; -@@ -3813,6 +4369,8 @@ static int platform_gpio_count(struct device *dev, const char *con_id) +@@ -3813,6 +4340,8 @@ static int platform_gpio_count(struct device *dev, const char *con_id) struct gpiod_lookup *p; unsigned int count = 0; @@ -3380,7 +3378,7 @@ index 50abb1c20df0..e4648d29f26b 100644 table = gpiod_find_lookup_table(dev); if (!table) return -ENOENT; -@@ -3858,6 +4416,8 @@ struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, +@@ -3858,6 +4387,8 @@ struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, char prop_name[32]; /* 32 is max size of property name */ unsigned int i; @@ -3389,7 +3387,7 @@ index 50abb1c20df0..e4648d29f26b 100644 for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (con_id) snprintf(prop_name, sizeof(prop_name), "%s-%s", -@@ -3886,6 +4446,8 @@ int gpiod_count(struct device *dev, const char *con_id) +@@ -3886,6 +4417,8 @@ int gpiod_count(struct device *dev, const char *con_id) { int count = -ENOENT; @@ -3398,7 +3396,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) count = of_gpio_get_count(dev, con_id); else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) -@@ -3911,6 +4473,8 @@ EXPORT_SYMBOL_GPL(gpiod_count); +@@ -3911,6 +4444,8 @@ EXPORT_SYMBOL_GPL(gpiod_count); struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags) { @@ -3407,7 +3405,7 @@ index 50abb1c20df0..e4648d29f26b 100644 return gpiod_get_index(dev, con_id, 0, flags); } EXPORT_SYMBOL_GPL(gpiod_get); -@@ -3951,6 +4515,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, +@@ -3951,6 +4486,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, { int ret; @@ -3416,7 +3414,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (lflags & GPIO_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); -@@ -4121,6 +4687,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, +@@ -4121,6 +4658,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, struct gpio_desc *desc = ERR_PTR(-ENODEV); int ret; @@ -3425,7 +3423,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (!fwnode) return ERR_PTR(-EINVAL); -@@ -4178,6 +4746,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, +@@ -4178,6 +4717,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, { struct gpio_desc *desc; @@ -3434,7 +3432,7 @@ index 50abb1c20df0..e4648d29f26b 100644 desc = gpiod_get_index(dev, con_id, index, flags); if (IS_ERR(desc)) { if (PTR_ERR(desc) == -ENOENT) -@@ -4204,6 +4774,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, +@@ -4204,6 +4745,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, int hwnum; int ret; @@ -3443,7 +3441,7 @@ index 50abb1c20df0..e4648d29f26b 100644 gc = gpiod_to_chip(desc); hwnum = gpio_chip_hwgpio(desc); -@@ -4235,6 +4807,8 @@ static void gpiochip_free_hogs(struct gpio_chip *gc) +@@ -4235,6 +4778,8 @@ static void gpiochip_free_hogs(struct gpio_chip *gc) { int id; @@ -3452,7 +3450,7 @@ index 50abb1c20df0..e4648d29f26b 100644 for (id = 0; id < gc->ngpio; id++) { if (test_bit(FLAG_IS_HOGGED, &gc->gpiodev->descs[id].flags)) gpiochip_free_own_desc(&gc->gpiodev->descs[id]); -@@ -4263,6 +4837,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, +@@ -4263,6 +4808,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, struct gpio_chip *gc; int count, bitmap_size; @@ -3461,7 +3459,7 @@ index 50abb1c20df0..e4648d29f26b 100644 count = gpiod_count(dev, con_id); if (count < 0) return ERR_PTR(count); -@@ -4383,6 +4959,8 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, +@@ -4383,6 +4930,8 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, { struct gpio_descs *descs; @@ -3470,7 +3468,7 @@ index 50abb1c20df0..e4648d29f26b 100644 descs = gpiod_get_array(dev, con_id, flags); if (PTR_ERR(descs) == -ENOENT) return NULL; -@@ -4399,6 +4977,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); +@@ -4399,6 +4948,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); */ void gpiod_put(struct gpio_desc *desc) { @@ -3479,7 +3477,7 @@ index 50abb1c20df0..e4648d29f26b 100644 if (desc) gpiod_free(desc); } -@@ -4412,6 +4992,8 @@ void gpiod_put_array(struct gpio_descs *descs) +@@ -4412,6 +4963,8 @@ void gpiod_put_array(struct gpio_descs *descs) { unsigned int i; @@ -3488,7 +3486,7 @@ index 50abb1c20df0..e4648d29f26b 100644 for (i = 0; i < descs->ndescs; i++) gpiod_put(descs->desc[i]); -@@ -4423,6 +5005,8 @@ static int __init gpiolib_dev_init(void) +@@ -4423,6 +4976,8 @@ static int __init gpiolib_dev_init(void) { int ret; @@ -3497,7 +3495,7 @@ index 50abb1c20df0..e4648d29f26b 100644 /* Register GPIO sysfs bus */ ret = bus_register(&gpio_bus_type); if (ret < 0) { -@@ -4442,13 +5026,13 @@ static int __init gpiolib_dev_init(void) +@@ -4442,13 +4997,13 @@ static int __init gpiolib_dev_init(void) #if IS_ENABLED(CONFIG_OF_DYNAMIC) && IS_ENABLED(CONFIG_OF_GPIO) WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier)); @@ -3513,7 +3511,7 @@ index 50abb1c20df0..e4648d29f26b 100644 static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) { -@@ -4575,4 +5159,4 @@ static int __init gpiolib_debugfs_init(void) +@@ -4575,4 +5130,4 @@ static int __init gpiolib_debugfs_init(void) } subsys_initcall(gpiolib_debugfs_init); diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index 530b47aae..73c5071fb 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -15,7 +15,7 @@ index dcecc9f6e33f..ed7d58d68ba6 100644 endmenu diff --git a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile 2024-05-02 12:06:58.097355696 +0000 -+++ b/drivers/Makefile 2024-07-11 13:56:04.938122389 +0000 ++++ b/drivers/Makefile 2024-07-24 12:47:03.482141344 +0000 @@ -193,0 +194,5 @@ +# +# @@ -48,10 +48,10 @@ index 0000000..2580e02 +obj-$(CONFIG_TEGRA_GPIO_GUEST_PROXY) += gpio-guest-proxy.o diff --git a/drivers/gpio-guest-proxy/gpio-guest-proxy.c b/drivers/gpio-guest-proxy/gpio-guest-proxy.c new file mode 100644 -index 0000000..6b385ed +index 0000000..8696a99 --- /dev/null +++ b/drivers/gpio-guest-proxy/gpio-guest-proxy.c -@@ -0,0 +1,794 @@ +@@ -0,0 +1,851 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -79,20 +79,20 @@ index 0000000..6b385ed + +#include "../gpio-host-proxy/gpio-host-proxy.h" + -+#define DEVICE_NAME "gpio-guest" // Device name. -+#define CLASS_NAME "char" ++#define DEVICE_NAME "gpio-guest" // Device name. ++#define CLASS_NAME "chardrv" // < The device class -- this is a character device driver + -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Kim Sandstrom"); -+MODULE_DESCRIPTION("NVidia GPIO Guest Proxy Kernel Module"); -+MODULE_VERSION("0.0"); ++MODULE_LICENSE("GPL"); ///< The license type -- this affects available functionality ++MODULE_AUTHOR("Kim Sandström"); ///< The author -- visible when you use modinfo ++MODULE_DESCRIPTION("NVidia GPIO Guest Proxy Kernel Module"); ///< The description -- see modinfo ++MODULE_VERSION("0.0"); ///< A version number to inform users + +#define GPIO_DEBUG +#define GPIO_DEBUG_VERBOSE // also activates deb_verbose commands + +#ifdef GPIO_DEBUG + #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) -+ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__ , kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) + #define deb_error(fmt, ...) printk(KERN_ERR "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__ , kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) @@ -110,6 +110,7 @@ index 0000000..6b385ed + ); +#else + #define deb_verbose(fmt, ...) ++ #define hexDump(...) +#endif + +// MEM_SIZE defined in gpio-host-proxy.h @@ -122,8 +123,14 @@ index 0000000..6b385ed +extern int gpio_outloud; +extern uint64_t gpio_vpa; + -+static char return_buffer[MEM_SIZE]; // using the same size as the input buffer -+static int *return_value = (int *)return_buffer; // local return value for chardev interaction ++#define RET_SIZE 8 // should be sizeof(uint64_t) ++// static char return_buffer[MEM_SIZE]; // using the same size as the input buffer ++static char return_buffer[RET_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0}; ++static unsigned int return_size = 0; ++ ++_Static_assert(sizeof(uint64_t) == RET_SIZE, "return size assertion for RET_SIZE failed"); ++_Static_assert(sizeof(u32) == sizeof(int), "return size assertion for int failed"); ++_Static_assert(sizeof(volatile void __iomem *) == sizeof(uint64_t), "return size assertion for iomem pointer failed"); + +/* functions redirected to guest-proxy from gpio-tegra186.c + * from setup of gpio_chip in tegra186_gpio_probe @@ -134,7 +141,7 @@ index 0000000..6b385ed + * mgs: is a pointer to data -- in practice tegra_gpio_pt and tegra_gpio_pt_extended structs + * generic return: poiter to return data, may be NULL if we do not expect a return value + */ -+void guest_chardev_transfer(void *msg, char msg_len, int *generic_return) ++inline void guest_chardev_transfer(void *msg, char msg_len, void *generic_return, int ret_len) +{ + // encode message length into first byte (LSB bit is preserved for other use) + unsigned char *length = (unsigned char *)msg; @@ -145,7 +152,7 @@ index 0000000..6b385ed + } + + deb_verbose("PT transfer signal is: %c", *((char *)msg + 1)); -+ hexDump(DEVICE_NAME, "PT transfer (to host)", msg, msg_len); ++ hexDump(DEVICE_NAME, "PT transfer (message to host)", msg, msg_len); + + // Execute the request by copying to io memory + memcpy_toio(mem_iova, msg, msg_len); @@ -155,11 +162,24 @@ index 0000000..6b385ed + // check if we expect a return value + if(generic_return) { + // Read response from io_buffer -+ memcpy_fromio(generic_return, mem_iova, sizeof(*generic_return)); // 32 bits -+ hexDump(DEVICE_NAME, "PT transfer (from host)", generic_return, sizeof(*generic_return) ); } ++ // hexDump(DEVICE_NAME, "GPIO: PT dump mem_iova", (char *)mem_iova, MEM_SIZE); ++ // deb_verbose("ret_len = %d\n", ret_len); ++ memcpy_fromio(generic_return, (void *)((char *)mem_iova + RETURN_OFF), ret_len); // 32 bits except for tegra186_gpio_get_base_redirect 64 bits ++ hexDump(DEVICE_NAME, "GPIO: PT transfer (retrieved retval from host)", generic_return, ret_len ); } +} + -+_Static_assert(sizeof(u32) == sizeof(int), "return size assertion failed"); ++void __iomem *tegra186_gpio_get_base_redirect(unsigned char id, unsigned int pin) { ++ void __iomem *ret_ptr = (void __iomem *)0x01234567ABADFACE; ++ struct tegra_getbase_pt msg; ++ ++ msg.signal = TEGRA_186_GETBASE; ++ msg.chipnum = id; ++ msg.pin = pin; ++ ++ guest_chardev_transfer(&msg, sizeof(msg), &ret_ptr, sizeof(ret_ptr)); ++ deb_verbose(" 0x%p", ret_ptr); ++ return ret_ptr; ++} + +// redirect static inline u32 readl_x(const volatile void __iomem *addr) +inline u32 readl_redirect( void * addr, const unsigned char rwltype) { @@ -172,8 +192,8 @@ index 0000000..6b385ed + rwlmsg.address = addr; + rwlmsg.value = 0; // value field is not used for readl + -+ guest_chardev_transfer(&rwlmsg, sizeof(rwlmsg), &ret); -+ deb_verbose("return value: 0x%X", ret); ++ guest_chardev_transfer(&rwlmsg, sizeof(rwlmsg), &ret, sizeof(ret)); ++ deb_verbose(" 0x%X", ret); + return ret; +} +EXPORT_SYMBOL_GPL(readl_redirect); @@ -188,7 +208,7 @@ index 0000000..6b385ed + rwlmsg.address = addr; + rwlmsg.value = value; + -+ guest_chardev_transfer(&rwlmsg, sizeof(rwlmsg), NULL); ++ guest_chardev_transfer(&rwlmsg, sizeof(rwlmsg), NULL, 0); +} +EXPORT_SYMBOL_GPL(writel_redirect); + @@ -201,7 +221,7 @@ index 0000000..6b385ed + msg.level = 0; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); + return ret; +} + @@ -213,7 +233,7 @@ index 0000000..6b385ed + msg.level = 0; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), NULL); ++ guest_chardev_transfer(&msg, sizeof(msg), NULL, 0); +} + +int tegra186_gpio_get_direction_redirect(struct gpio_chip *chip, @@ -226,7 +246,8 @@ index 0000000..6b385ed + msg.level = 0; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); ++ deb_verbose(" 0x%X", ret); + return ret; +} + @@ -240,7 +261,8 @@ index 0000000..6b385ed + msg.level = 0; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); ++ deb_verbose(" 0x%X", ret); + return ret; +} + @@ -254,7 +276,8 @@ index 0000000..6b385ed + msg.level = level; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); ++ deb_verbose(" 0x%X", ret); + return ret; +} + @@ -267,7 +290,8 @@ index 0000000..6b385ed + msg.level = 0; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); ++ deb_verbose(" 0x%X", ret); + return ret; +} + @@ -280,7 +304,7 @@ index 0000000..6b385ed + msg.level = level; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), NULL); ++ guest_chardev_transfer(&msg, sizeof(msg), NULL, 0); +} + +void tegra186_gpio_set_by_name_redirect(const char *name, unsigned int offset, @@ -292,7 +316,7 @@ index 0000000..6b385ed + msg.level = level; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), NULL); ++ guest_chardev_transfer(&msg, sizeof(msg), NULL, 0); +} + +int tegra186_gpio_set_config_redirect(struct gpio_chip *chip, @@ -306,7 +330,7 @@ index 0000000..6b385ed + msg.level = 0; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); + return ret; +} + @@ -320,7 +344,7 @@ index 0000000..6b385ed + msg.level = 0; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); + return ret; +} + @@ -334,7 +358,7 @@ index 0000000..6b385ed + msg.level = 0; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); + return ret; +} + @@ -348,7 +372,7 @@ index 0000000..6b385ed + msg.level = 0; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); + return ret; +} + @@ -362,7 +386,7 @@ index 0000000..6b385ed + msg.level = 0; + msg.offset = 0; + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret); ++ guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); + return ret; +} + @@ -520,138 +544,142 @@ index 0000000..6b385ed + * Reads from device, displays in userspace, and deletes the read data + */ +static ssize_t read(struct file *filp, char *buf, size_t len, loff_t *offset) { -+ int remaining_length = sizeof(*return_value) - *offset; ++ int remaining_length = return_size - *offset; + -+ deb_info("guest: read gpio chardev\n"); -+ deb_verbose("guest: read op: len = %ld, offset = %lld, *return_value = 0x%X\n", len, *offset, *return_value); ++ deb_info("guest: read gpio chardev\n"); ++ deb_verbose("guest: read op: len = %ld, offset = %lld, *return_buffer = 0x%016llX\n", len, *offset, *(uint64_t *)return_buffer); ++ hexDump (DEVICE_NAME, "Chardev (guest read) dump buffer", return_buffer, len); ++ //deb_verbose("guest: read op: len = %ld, offset = %lld, *return_value = 0x%016llX\n", len, *offset, *return_value); ++ // hexDump (DEVICE_NAME, "Chardev (guest read) dump buffer", (char *)return_value, return_size); + -+ if ( remaining_length < 0 ) { -+ deb_info("guest: unrecoverable length *error*, remaining_length = %d\n", remaining_length); -+ return -EINVAL; -+ } ++ if ( remaining_length < 0 ) { ++ deb_info("guest: unrecoverable length *error*, remaining_length = %d\n", remaining_length); ++ return -EINVAL; ++ } + -+ if ( len > remaining_length ) { -+ deb_info("guest: recoverable length *error*, len = %ld, remaining_length = %d, rlen = %ld\n", len, remaining_length, sizeof(*return_value)); -+ len = remaining_length - *offset; -+ } ++ if ( len > remaining_length ) { ++ deb_info("guest: recoverable length *error*, len = %ld, remaining_length = %d, return_size = %d\n", len, remaining_length, return_size); ++ len = remaining_length - *offset; ++ } + -+ if (copy_to_user(buf + *offset, return_buffer + *offset, len)) { -+ deb_info("guest: failed to copy to user\n"); -+ return -EFAULT; -+ } ++ if (copy_to_user(buf + *offset, (char *)return_buffer + *offset, len)) { ++ deb_info("guest: failed to copy to user\n"); ++ return -EFAULT; ++ } + -+ *offset += len; ++ *offset += len; + -+ // Check if all data was copied -+ if (sizeof(*return_value) < len) { -+ deb_info("guest: not all bytes were copied\n"); -+ // If not, set the error status and return the number of bytes actually copied -+ // return -EINVAL; -+ } ++ // Check if all data was copied ++ if (remaining_length > len) { ++ deb_info("guest: not all bytes were copied\n"); ++ // If not, set the error status and return the number of bytes actually copied ++ // return -EINVAL; ++ } ++ else if (remaining_length == len) { ++ deb_verbose("reset return_size\n"); ++ return_size = 0; ++ memset(return_buffer, 0, RET_SIZE); ++ } + -+ // Otherwise, indicate success by returning the number of bytes requested -+ return len; ++ // Indicate success by returning the number of bytes read ++ return len; +} + +/* + * Writes to the device + */ + -+// TODO this is shared code with host proxy include function from there -+// look at line 335 of this file -+// extern static ssize_t chardev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) -+ +static ssize_t write(struct file *filep, const char *buffer, size_t len, loff_t *offset) +{ -+ int ret; -+ // unsigned long int ret_l; + struct tegra_gpio_pt *kbuf = NULL; + tegra_gpio_pt_extended *kbuf_ext = NULL; -+ // unsigned char *mask; ++ struct tegra_getbase_pt *kbuf_getbase = NULL; ++ char *buffer_pos = (char *)buffer; ++ // unsigned char *mask; ++ void __iomem *ret_ptr = NULL; ++ int ret_int; // 32 bits ++ int ret; + + /* + static struct file *file; + static struct inode *inode = NULL; + */ + struct gpio_chip *chip; -+ /* + #ifdef GPIO_DEBUG_VERBOSE -+ struct gpio_chip *chip_alt; ++ // struct gpio_chip *chip_alt; + #endif -+ */ -+ -+ char *buffer_pos = (char *)buffer; + + deb_info("## writeing %zu bytes to chardev ##", len); + -+ // We allow tegra_gpio_pt alone or with tegra_gpio_pt_extended -+ if( len != sizeof(struct tegra_gpio_pt) && len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) { -+ pr_err("Illegal chardev data length. Expected %ld or %ld, got %ld", sizeof(struct tegra_gpio_pt), sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), len); -+ hexDump (DEVICE_NAME, "Chardev (guest) input error", buffer, len); -+ return -ENOEXEC; -+ return 0; ++ if( len != sizeof(struct tegra_gpio_pt) && ++ len != sizeof(struct tegra_getbase_pt) && ++ len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) { ++ pr_err("Illegal chardev data length. Expected %ld, %ld, %ld or %ld, but got %ld\n", ++ sizeof(struct tegra_gpio_pt), ++ sizeof(struct tegra_getbase_pt), ++ sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), ++ sizeof(struct tegra_readl_writel), ++ len); ++ hexDump (DEVICE_NAME, "Chardev (guest) input error", buffer, len); ++ return -ENOEXEC; // kbuf not allocated yet + } + -+ if(!offset) { -+ pr_err("offset pointer is null, ignoring offset\n"); -+ } -+ else { -+ buffer_pos += (*offset); -+ } ++ if(!offset) { ++ pr_err("offset pointer is NULL, ignoring offset\n"); ++ } ++ else { ++ buffer_pos += (*offset); ++ } + + kbuf = kmalloc(len, GFP_KERNEL); + if ( !kbuf ) { -+ pr_err("kbuf memory allocation failed\n"); -+ return -ENOMEM; ++ pr_err("kbuf memory allocation failed\n"); ++ len = -ENOMEM; ++ goto exit; + } + memset(kbuf, 0, len); ++ memset(return_buffer, 0, RET_SIZE); + + // Copy header -+ if (copy_from_user(kbuf, buffer_pos, sizeof(struct tegra_gpio_pt))) { -+ pr_err("copy_from_user failed\n"); -+ kfree(kbuf); -+ return -ENOMEM; -+ } -+ // deb_verbose("kbuf is set up at kbuf=%p", kbuf); -+ -+ /* not necessary; only present in input to qemu passthough device -+ // mask away length from first byte where it is encoded in the top 7 bits (chipnum is lowest bit) -+ mask = (unsigned char *)kbuf; -+ *mask = *mask && 0x01; -+ */ ++ if (copy_from_user(kbuf, buffer_pos, len)) { ++ pr_err("copy_from_user failed\n"); ++ len = -ENOMEM; ++ goto exit; ++ } ++ buffer_pos += RETURN_OFF; + -+ // we are not checking if tegra_gpio_pt_extended is used, we only check for memory allocation -+ if( len == (sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) ) { -+ kbuf_ext = (tegra_gpio_pt_extended *)(kbuf + 1); -+ deb_verbose("kbuf_ext is set up at kbuf_ext=%p", kbuf_ext); -+ } ++ // we are not checking if tegra_gpio_pt_extended is used, we only check for memory allocation ++ if( len == (sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) ) { ++ kbuf_ext = (tegra_gpio_pt_extended *)(kbuf + 1); ++ deb_verbose("kbuf_ext is set up at kbuf_ext=%p", kbuf_ext); ++ } + -+ // print copied user parameters -+ hexDump (DEVICE_NAME, "Chardev input", kbuf, len); ++ // print copied user parameters ++ hexDump (DEVICE_NAME, "Chardev input", kbuf, len); + -+ // make gpio-host type call to gpio ++ // make chardev type call to gpio + deb_verbose("Passthrough from guest with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); + + chip = find_chip_by_id(kbuf->chipnum); ++ /* ++ #ifdef GPIO_DEBUG_VERBOSE ++ chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); ++ if(chip != chip_alt) { ++ deb_debug("conflicting chip pointers -- primary %p, alternative %p", chip, chip_alt); ++ chip = chip_alt; // we assume find_chip_by_name is more reliable ++ } ++ #endif ++ */ ++ if(!chip) { ++ pr_err("chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); ++ len = -ENODEV; ++ goto exit; ++ } + + switch (kbuf->signal) { + case GPIO_REQ: -+ /* -+ #ifdef GPIO_DEBUG_VERBOSE -+ chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); -+ if(chip != chip_alt) { -+ deb_debug("conflicting chip pointers -- primary %p, alternative %p", chip, chip_alt); -+ chip = chip_alt; // we assume find_chip_by_name is more reliable -+ } -+ #endif -+ */ -+ if(!chip) { -+ pr_err("In GPIO_REQ, chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); -+ kfree(kbuf); -+ return -ENODEV; -+ } + deb_verbose("GPIO_REQ, using GPIO chip %s, for device %d\n", chip->label, kbuf->chipnum); -+ ret = chip->request(chip, kbuf->offset); ++ ret_int = chip->request(chip, kbuf->offset); + goto end; + break; + case GPIO_FREE: @@ -662,22 +690,22 @@ index 0000000..6b385ed + break; + case GPIO_GET_DIR: + deb_verbose("GPIO_GET_DIR\n"); -+ ret = chip->get_direction(chip, kbuf->offset); ++ ret_int = chip->get_direction(chip, kbuf->offset); + goto retval; + break; + case GPIO_SET_IN: + deb_verbose("GPIO_SET_IN\n"); -+ ret = chip->direction_input(chip, kbuf->offset); ++ ret_int = chip->direction_input(chip, kbuf->offset); + goto retval; + break; + case GPIO_SET_OUT: + deb_verbose("GPIO_SET_OUT\n"); -+ ret = chip->direction_output(chip, kbuf->offset, kbuf->level); ++ ret_int = chip->direction_output(chip, kbuf->offset, kbuf->level); + goto retval; + break; + case GPIO_GET: + deb_verbose("GPIO_GET\n"); -+ ret = chip->get(chip, kbuf->offset); ++ ret_int = chip->get(chip, kbuf->offset); + goto retval; + break; + case GPIO_SET: @@ -692,12 +720,12 @@ index 0000000..6b385ed + break; + case GPIO_TIMESTAMP_CTRL: + deb_verbose("GPIO_TIMESTAMP_CTRL\n"); -+ ret = chip->timestamp_control(chip, kbuf->offset, kbuf->level); // mapping level onto enable ++ ret_int = chip->timestamp_control(chip, kbuf->offset, kbuf->level); // mapping level onto enable + goto retval; + break; + case GPIO_TIMESTAMP_READ: + deb_verbose("GPIO_TIMESTAMP_READ\n"); -+ ret = chip->timestamp_read(chip, kbuf->offset, (u64 *)buffer_pos); // timestamp is u64, return value as pointer ++ ret = chip->timestamp_read(chip, kbuf->offset, (uint64_t *)buffer_pos); // timestamp is u64, return value as pointer + if(ret) { + pr_err("GPIO_TIMESTAMP_READ error\n"); + goto end; @@ -709,17 +737,29 @@ index 0000000..6b385ed + deb_verbose("GPIO_SUSPEND_CONF\n"); + if(!kbuf_ext) { + pr_err("Parameter error in GPIO_SUSPEND_CONF\n"); -+ return -EINVAL; ++ len = -EINVAL; ++ goto exit; + } -+ ret = chip->suspend_configure(chip, kbuf->offset, kbuf_ext->dflags); ++ ret_int = chip->suspend_configure(chip, kbuf->offset, kbuf_ext->dflags); + goto retval; + break; + case GPIO_ADD_PINRANGES: + deb_verbose("GPIO_ADD_PINRANGES\n"); -+ ret = chip->add_pin_ranges(chip); ++ ret_int = chip->add_pin_ranges(chip); + goto retval; + break; -+ }; ++ case TEGRA_186_GETBASE: ++ deb_verbose("TEGRA_186_GETBASE\n"); ++ kbuf_getbase = (void *)kbuf; ++ ret_ptr = tegra186_gpio_get_base_redirect(kbuf_getbase->chipnum, kbuf_getbase->pin); ++ goto retptr; // 64 bit? ++ break; ++ default: ++ pr_err("GPIO, Unknown passthough signal\n"); ++ len = -EPERM; ++ goto exit; ++ break; ++ } + + /* ioctl signals use flieops because it relises on the standard gpio chardevs + static const struct file_operations gpio_fileops = { @@ -742,29 +782,36 @@ index 0000000..6b385ed + */ + /* commands to ioctl below (the std gpio chardev) + * not fully implemented ++ * linehandle_create -- when userspace requests output (called by gpio_ioctl) -- bypasses the chardev ++ * linehandle_ioctl -- linehandle_ioctl when userspace does actual io (toggles pin) ++ * cmd: ++ * GPIOHANDLE_GET_LINE_VALUES_IOCTL, ++ * GPIOHANDLE_SET_LINE_VALUES_IOCTL, ++ * GPIOHANDLE_SET_CONFIG_IOCTL ++ * arg: user input or output ++ */ ++/* ++ // We could want to use the stock gpio chardev (/dev/gpiochip0 and /dev/gpiochip1) /bc userspace functions use it ++ // this code is not yet complete and it mey be better to use the stock devices directly. ++ case GPIO_CHARDEV_OPEN: // .open = gpio_chrdev_open + file = filp_open(tegra_chiplabel[kbuf->chipnum], O_RDWR, 0); + if (IS_ERR(file)) { + pr_err("GPIO, failed to open chardev for chip %s: %ld", tegra_chiplabel[kbuf->chipnum], PTR_ERR(file)); -+ kfree(kbuf); -+ return -ENOENT; -+ file = filp_open(tegra_chiplabel[kbuf->chipnum], O_RDWR, 0); -+ if (IS_ERR(file)) { -+ pr_err("GPIO, failed to open chardev for chip %s: %ld", tegra_chiplabel[kbuf->chipnum], PTR_ERR(file)); -+ kfree(kbuf); -+ return -ENOENT; ++ len = -ENOENT; ++ goto exit; + } + // note: inode and file are static variables + inode = file->f_path.dentry->d_inode; + // defined as: static int gpio_chrdev_open(struct inode *inode, struct file *file) -+ ret = file->f_op->open(inode, file); ++ ret_int = file->f_op->open(inode, file); + goto retval; + break; + case GPIO_CHARDEV_IOCTL: // .unlocked_ioctl = gpio_ioctl + // user space triggers gpio_ioctl -- it is .unlocked_ioctl on the chardev + if( !file ) { + pr_err("GPIO, chardev file was expected to be open\n"); -+ kfree(kbuf); -+ return -ENOENT; ++ len = -ENOENT; ++ goto exit; + } + // defined as: static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + ret_l = file->f_op->unlocked_ioctl(file, kbuf->cmd, kbuf_ext->arg); // arg is pointer data which should have been copied from userspace @@ -773,78 +820,88 @@ index 0000000..6b385ed + case GPIO_CHARDEV_RELEASE: // .release = gpio_chrdev_release + if( !file ) { + pr_err("GPIO, chardev file was expected to be open\n"); -+ kfree(kbuf); -+ return -ENOENT; ++ len = -ENOENT; ++ goto exit; + } + // defined as: static int gpio_chrdev_release(struct inode *inode, struct file *file) -+ ret = file->f_op->release(inode, file); ++ ret_int = file->f_op->release(inode, file); + goto retval; + break; + case GPIO_CHARDEV_POLL: // .poll = lineinfo_watch_poll + if( !file ) { + pr_err("GPIO, chardev file was expected to be open\n"); -+ kfree(kbuf); -+ return -ENOENT; ++ len = -ENOENT; ++ goto exit; + } + // defined as: static __poll_t lineinfo_watch_poll(struct file *file, struct poll_table_struct *pollt) -+ ret = file->f_op->poll(file, kbuf_ext->poll); // TODO arg is pointer data which should have been copied ++ ret_int = file->f_op->poll(file, kbuf_ext->poll); // TODO arg is pointer data which should have been copied + goto retval; // __poll_t is of size unsigned int + break; + case GPIO_CHARDEV_READ: // .read = lineinfo_watch_read + if( !file ) { + pr_err("GPIO, chardev file was expected to be open\n"); -+ kfree(kbuf); -+ return -ENOENT; ++ len = -ENOENT; ++ goto exit; + } + // defined as: static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, size_t count, loff_t *off) + ret = file->f_op->read(file, buffer_pos, kbuf_ext->count, NULL); // + if (ret) { + pr_err("Reading lineinfo returned zero\n"); -+ kfree(kbuf); -+ return -EFAULT; ++ len = -EFAULT; ++ goto exit; + } -+ return -ENXIO; ++ break; + case GPIO_CHARDEV_OWNER: // .owner = THIS_MODULE + if (copy_to_user(buffer_pos, file->f_op->owner->name, strlen(file->f_op->owner->name)+1)) { + pr_err("GPIO, copying user return value failed\n"); -+ kfree(kbuf); -+ return -EFAULT; ++ len = -EFAULT; ++ goto exit; + } + break; + default: -+ pr_err("GPIO, Illegal proxy signal type\n"); -+ kfree(kbuf); -+ return -EPERM; ++ pr_err("GPIO, Unknown passthough signal\n"); ++ len = -EPERM; ++ goto exit; + break; + }; + }; ++ */ + + goto end; + -+ retlong: -+ if ( copy_to_user(buffer_pos, &ret_l, sizeof(ret_l)) ) { -+: pr_err("GPIO, copying int user return value failed\n"); -+ kfree(kbuf); -+ return -EFAULT; -+ }; -+ */ -+ ++ retptr: ++ return_size = sizeof(ret_ptr); ++ memcpy(return_buffer, &ret_ptr, return_size); ++ deb_verbose("retval pointer (guest): 0x%p, 0x%016llX", ret_ptr, (uint64_t)ret_ptr); + goto end; + + retval: -+ *return_value = ret; -+ deb_verbose("retval (guest): 0x%X", ret); -+ if ( copy_to_user((void *)buffer, &ret, sizeof(ret)) ) { -+ pr_err("GPIO, copying int user return value failed\n"); -+ kfree(kbuf); -+ return -EFAULT; -+ }; -+ -+ goto end; ++ return_size = sizeof(ret_int); ++ memcpy(return_buffer, &ret_int, return_size); ++ deb_verbose("retval int (guest): 0x%X", ret_int); + + end: ++ *offset = RETURN_OFF; ++ len = *offset; ++ len += sizeof(return_buffer); ++ ++ if (MEM_SIZE >= return_size + RETURN_OFF) { ++ if ( (ret = copy_to_user( (char *)buffer_pos, return_buffer, sizeof(return_buffer))) ) { ++ pr_err("GPIO, copying user return value failed: 0x%08X\n", ret); ++ len = -EFAULT; ++ goto exit; ++ } ++ len += sizeof(return_buffer); ++ } else { ++ len = -EINVAL; // Buffer too small ++ goto exit; ++ } ++ // deb_verbose("retval copied to buffer (guest): 0x%016llX", *(uint64_t *)return_buffer); ++ // hexDump(DEVICE_NAME, "Chardev (guest write) dump buffer", return_buffer, MEM_SIZE); ++ ++ exit: + kfree(kbuf); -+ return len; ++ return len; // return length of read data +} diff --git a/drivers/gpio-host-proxy/Kconfig b/drivers/gpio-host-proxy/Kconfig new file mode 100644 @@ -871,10 +928,10 @@ index 0000000..c2e0184 +obj-$(CONFIG_TEGRA_GPIO_HOST_PROXY) += gpio-host-proxy.o diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.c b/drivers/gpio-host-proxy/gpio-host-proxy.c new file mode 100644 -index 0000000..b26862b +index 0000000..fd3956b --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.c -@@ -0,0 +1,717 @@ +@@ -0,0 +1,757 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO host Proxy Kernel Module @@ -882,16 +939,17 @@ index 0000000..b26862b + * (c) 2023 Kim Sandstrom kim.sandstrom@unikie.com + * + **/ ++ +#include // Core header for modules. +#include // Supports driver model. +#include // Kernel header for convenient functions. +#include // File-system support. +#include // User access copy function support. ++#include +#include +//#include -+#include -+#include +#include ++#include +#include +#include +#include @@ -905,10 +963,10 @@ index 0000000..b26862b +#define DEVICE_NAME "gpio-host" // Device name. +#define CLASS_NAME "chardrv" // < The device class -- this is a character device driver + -+MODULE_LICENSE("GPL\n"); ///< The license type -- this affects available functionality -+MODULE_AUTHOR("Kim Sandström\n"); ///< The author -- visible when you use modinfo -+MODULE_DESCRIPTION("NVidia GPIO Host Proxy Kernel Module\n"); ///< The description -- see modinfo -+MODULE_VERSION("0.0\n"); ///< A version number to inform users ++MODULE_LICENSE("GPL"); ///< The license type -- this affects available functionality ++MODULE_AUTHOR("Kim Sandström"); ///< The author -- visible when you use modinfo ++MODULE_DESCRIPTION("NVidia GPIO Host Proxy Kernel Module"); ///< The description -- see modinfo ++MODULE_VERSION("0.0"); ///< A version number to inform users + +#define GPIO_DEBUG +#define GPIO_DEBUG_VERBOSE // also activates deb_verbose commands @@ -916,9 +974,11 @@ index 0000000..b26862b +#ifdef GPIO_DEBUG + #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) + #define deb_debug(fmt, ...) printk(KERN_DEBUG "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) ++ #define deb_error(fmt, ...) printk(KERN_ERR "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__ , kbasename(__FILE__), ##__VA_ARGS__) +#else + #define deb_info(fmt, ...) + #define deb_debug(fmt, ...) ++ #define deb_error(fmt, ...) +#endif + +#ifdef GPIO_DEBUG_VERBOSE @@ -932,6 +992,18 @@ index 0000000..b26862b +const char *tegra_chiplabel[2] = {TEGRA_GPIO_LABEL,TEGRA_GPIO_AON_LABEL}; +EXPORT_SYMBOL_GPL(tegra_chiplabel); + ++extern inline u32 readl_execute_base( void * addr); ++extern inline void writel_execute_base( u32 value, void * addr); ++ ++#define RET_SIZE 8 // should be sizeof(uint64_t) ++// static char return_buffer[MEM_SIZE]; // using the same size as the input buffer ++static char return_buffer[RET_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0}; ++static unsigned int return_size = 0; ++ ++_Static_assert(sizeof(uint64_t) == RET_SIZE, "return size assertion for RET_SIZE failed"); ++_Static_assert(sizeof(u32) == sizeof(int), "return size assertion for int failed"); ++_Static_assert(sizeof(volatile void __iomem *) == sizeof(uint64_t), "return size assertion for iomem pointer failed"); ++ +/** + * Important variables that store data and keep track of relevant information. + */ @@ -948,9 +1020,6 @@ index 0000000..b26862b +static ssize_t read(struct file *, char *, size_t, loff_t *); +static ssize_t write(struct file *, const char *, size_t, loff_t *); + -+static char return_buffer[MEM_SIZE]; // using the same size as the input buffer -+static int *return_value = (int *)return_buffer; // local return value for chardev interaction -+ +/** + * File operations structure and the functions it points to. + */ @@ -1169,151 +1238,154 @@ index 0000000..b26862b + * Reads from device, displays in userspace, and deletes the read data + */ +static ssize_t read(struct file *filp, char *buf, size_t len, loff_t *offset) { -+ int remaining_length = sizeof(*return_value) - *offset; ++ int remaining_length = return_size - *offset; + -+ deb_info("host: read gpio chardev\n"); -+ deb_verbose("host: read op: len = %ld, offset = %lld, *return_value = 0x%X\n", len, *offset, *return_value); ++ deb_info("host: read gpio chardev\n"); ++ deb_verbose("host: read op, len = %ld, offset = %lld, *return_buffer = 0x%016llX\n", len, *offset, *(uint64_t *)return_buffer); ++ hexDump (DEVICE_NAME, "Chardev (host read) dump buffer", return_buffer, len); ++ //deb_verbose("host: read op: len = %ld, offset = %lld, *return_value = 0x%016llX\n", len, *offset, *return_value); ++ // hexDump (DEVICE_NAME, "Chardev (host read) dump buffer", (char *)return_value, return_size); + -+ if ( remaining_length < 0 ) { -+ deb_info("host: unrecoverable length *error*, remaining_length = %d\n", remaining_length); -+ return -EINVAL; -+ } ++ if ( remaining_length < 0 ) { ++ deb_info("host: unrecoverable length *error*, remaining_length = %d\n", remaining_length); ++ return -EINVAL; ++ } + -+ if ( len > remaining_length ) { -+ deb_info("host: recoverable length *error*, len = %ld, remaining_length = %d, rlen = %ld\n", len, remaining_length, sizeof(*return_value)); -+ len = remaining_length - *offset; -+ } ++ if ( len > remaining_length ) { ++ deb_info("host: recoverable length *error*, len = %ld, remaining_length = %d, return_size = %d\n", len, remaining_length, return_size); ++ len = remaining_length - *offset; ++ } + -+ if (copy_to_user(buf + *offset, return_buffer + *offset, len)) { -+ deb_info("host: failed to copy to user\n"); -+ return -EFAULT; -+ } ++ if (copy_to_user(buf + *offset, (char *)return_buffer + *offset, len)) { ++ deb_info("host: failed to copy to user\n"); ++ return -EFAULT; ++ } + -+ *offset += len; ++ *offset += len; + -+ // Check if all data was copied -+ if (sizeof(*return_value) < len) { -+ deb_info("host: not all bytes were copied\n"); -+ // If not, set the error status and return the number of bytes actually copied -+ // return -EINVAL; -+ } -+ -+ // Otherwise, indicate success by returning the number of bytes requested -+ return len; ++ // Check if all data was copied ++ if (remaining_length > len) { ++ deb_info("host: not all bytes were copied\n"); ++ // If not, set the error status and return the number of bytes actually copied ++ // return -EINVAL; ++ } ++ else if (remaining_length == len) { ++ deb_verbose("reset return_size\n"); ++ return_size = 0; ++ memset(return_buffer, 0, RET_SIZE); ++ } ++ ++ // Indicate success by returning the number of bytes read ++ return len; +} + ++void __iomem *tegra186_gpio_get_base_execute(int id, unsigned int pin); // implemented in ./kernel-5.10/drivers/gpio/gpio-tegra186.c ++ +/* + * Writes to the device + */ + +static ssize_t write(struct file *filep, const char *buffer, size_t len, loff_t *offset) +{ -+ int ret; // 32 bits -+ // unsigned long int ret_l; + struct tegra_gpio_pt *kbuf = NULL; -+ struct tegra_readl_writel *kbuf_rw = NULL; ++ struct tegra_readl_writel *kbuf_rw = NULL; + tegra_gpio_pt_extended *kbuf_ext = NULL; -+ // unsigned char *mask; ++ struct tegra_getbase_pt *kbuf_getbase = NULL; ++ char *buffer_pos = (char *)buffer; ++ // unsigned char *mask; ++ void __iomem *ret_ptr = NULL; ++ int ret_int; // 32 bits ++ int ret; + + /* + static struct file *file; + static struct inode *inode = NULL; + */ + struct gpio_chip *chip; -+ #ifdef GPIO_DEBUG ++ #ifdef GPIO_DEBUG_VERBOSE + // struct gpio_chip *chip_alt; + #endif + -+ char *buffer_pos = (char *)buffer; -+ + deb_info("## writeing %zu bytes to chardev ##", len); + -+ // We allow tegra_gpio_pt alone or with tegra_gpio_pt_extended + if( len != sizeof(struct tegra_gpio_pt) && -+ len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) && -+ len != sizeof(struct tegra_readl_writel) ) { -+ pr_err("Illegal chardev data length. Expected %ld, %ld or %ld, but got %ld\n", -+ sizeof(struct tegra_gpio_pt), -+ sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), -+ sizeof(struct tegra_readl_writel), -+ len); -+ hexDump (DEVICE_NAME, "Chardev (host) input error", buffer, len); -+ return -ENOEXEC; // we dont want kernel panic ++ len != sizeof(struct tegra_getbase_pt) && ++ len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) && ++ len != sizeof(struct tegra_readl_writel) ) { ++ pr_err("Illegal chardev data length. Expected %ld, %ld, %ld or %ld, but got %ld\n", ++ sizeof(struct tegra_gpio_pt), ++ sizeof(struct tegra_getbase_pt), ++ sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), ++ sizeof(struct tegra_readl_writel), ++ len); ++ hexDump (DEVICE_NAME, "Chardev (host) input error", buffer, len); ++ return -ENOEXEC; // kbuf not allocated yet + } + -+ if(!offset) { -+ pr_err("offset pointer is null, ignoring offset\n"); -+ } -+ else { -+ buffer_pos += (*offset); -+ } ++ if(!offset) { ++ pr_err("offset pointer is NULL, ignoring offset\n"); ++ } ++ else { ++ buffer_pos += (*offset); ++ } + + kbuf = kmalloc(len, GFP_KERNEL); + if ( !kbuf ) { -+ pr_err("kbuf memory allocation failed\n"); -+ return -ENOMEM; ++ pr_err("kbuf memory allocation failed\n"); ++ len = -ENOMEM; ++ goto exit; + } + memset(kbuf, 0, len); ++ memset(return_buffer, 0, RET_SIZE); + + // Copy header -+ if (copy_from_user(kbuf, buffer_pos, sizeof(struct tegra_gpio_pt))) { -+ pr_err("copy_from_user failed\n"); -+ kfree(kbuf); -+ return -ENOMEM; -+ } -+ // deb_verbose("kbuf is set up at kbuf=%p", kbuf); ++ if (copy_from_user(kbuf, buffer_pos, len)) { ++ pr_err("copy_from_user failed\n"); ++ len = -ENOMEM; ++ goto exit; ++ } ++ buffer_pos += RETURN_OFF; + -+ /* this should already been done by the passthrough qemu device -+ // mask away length from first byte where it is encoded in the top 7 bits (chipnum is lowest bit) -+ mask = (unsigned char *)kbuf; -+ *mask = *mask && 0x01; -+ */ -+ -+ // we are not checking if tegra_gpio_pt_extended is used, we only check for memory allocation -+ if( len == (sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) ) { -+ kbuf_ext = (tegra_gpio_pt_extended *)(kbuf + 1); -+ deb_verbose("kbuf_ext is set up at kbuf_ext=%p", kbuf_ext); -+ } ++ // we are not checking if tegra_gpio_pt_extended is used, we only check for memory allocation ++ if( len == (sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) ) { ++ kbuf_ext = (tegra_gpio_pt_extended *)(kbuf + 1); ++ deb_verbose("kbuf_ext is set up at kbuf_ext=%p", kbuf_ext); ++ } + -+ // print copied user parameters -+ hexDump (DEVICE_NAME, "Chardev input", kbuf, len); ++ // print copied user parameters ++ hexDump (DEVICE_NAME, "Chardev input", kbuf, len); + -+ // make gpio-host type call to gpio ++ // make chardev type call to gpio + deb_verbose("Passthrough in host with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); + + switch (kbuf->signal) { + case GPIO_READL: + kbuf_rw = (struct tegra_readl_writel *)kbuf; -+ deb_verbose("readl debug A\n"); -+ if( !access_ok(kbuf_rw->address, sizeof(u32)) ) { -+ deb_info("cannot access address 0x%p", kbuf_rw->address); -+ ret = 0xDEADBEEF; -+ goto debug1; ++ if( kbuf_rw->address == 0 || !access_ok(kbuf_rw->address, sizeof(u32)) ) { ++ deb_info("*error* cannot access address 0x%p (probably an address in guest space)", kbuf_rw->address); ++ ret_int = 0xDEADBEEF; ++ goto readl_addr_error; + } -+ goto debug1; + switch (kbuf_rw->rwltype) { + case RWL_STD: -+ ret = (int)readl(kbuf_rw->address); ++ ret_int = (int)readl(kbuf_rw->address); + break; + case RWL_RAW: -+ ret = (int)__raw_readl(kbuf_rw->address); ++ ret_int = (int)__raw_readl(kbuf_rw->address); + break; + case RWL_RELAXED: -+ ret = (int)readl_relaxed(kbuf_rw->address); ++ ret_int = (int)readl_relaxed(kbuf_rw->address); + break; + } -+ debug1: -+ deb_verbose("readl debug B\n"); ++ readl_addr_error: + goto retval; + break; + case GPIO_WRITEL: + kbuf_rw = (struct tegra_readl_writel *)kbuf; -+ deb_verbose("writel debug A\n"); -+ if( !access_ok(kbuf_rw->address, sizeof(u32)) ) { -+ deb_info("cannot access address 0x%p", kbuf_rw->address); -+ goto debug2; ++ if( kbuf_rw->address == 0 || !access_ok(kbuf_rw->address, sizeof(u32)) ) { ++ deb_info("*error* cannot access address 0x%p (probably an address in guest space)", kbuf_rw->address); ++ goto writel_addr_error; + } -+ goto debug2; + switch (kbuf_rw->rwltype) { + case RWL_STD: + writel(kbuf_rw->value, kbuf_rw->address); @@ -1324,8 +1396,7 @@ index 0000000..b26862b + case RWL_RELAXED: + writel_relaxed(kbuf_rw->value, kbuf_rw->address); + break; -+ debug2: -+ deb_verbose("writel debug B\n"); ++ writel_addr_error: + goto end; + } + break; @@ -1333,25 +1404,25 @@ index 0000000..b26862b + // if switch above is triggered we will either goto retval or goto end + + chip = find_chip_by_id(kbuf->chipnum); ++ /* ++ #ifdef GPIO_DEBUG_VERBOSE ++ chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); ++ if(chip != chip_alt) { ++ deb_debug("conflicting chip pointers -- primary %p, alternative %p", chip, chip_alt); ++ chip = chip_alt; // we assume find_chip_by_name is more reliable ++ } ++ #endif ++ */ + if(!chip) { -+ pr_err("In GPIO_REQ, chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); -+ kfree(kbuf); -+ return -ENODEV; ++ pr_err("chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); ++ len = -ENODEV; ++ goto exit; + } + + switch (kbuf->signal) { + case GPIO_REQ: -+ /* -+ #ifdef GPIO_DEBUG_VERBOSE -+ chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); -+ if(chip != chip_alt) { -+ deb_debug("conflicting chip pointers -- primary %p, alternative %p", chip, chip_alt); -+ chip = chip_alt; // we assume find_chip_by_name is more reliable -+ } -+ #endif -+ */ + deb_verbose("GPIO_REQ, using GPIO chip %s, for device %d\n", chip->label, kbuf->chipnum); -+ ret = chip->request(chip, kbuf->offset); ++ ret_int = chip->request(chip, kbuf->offset); + goto end; + break; + case GPIO_FREE: @@ -1362,22 +1433,22 @@ index 0000000..b26862b + break; + case GPIO_GET_DIR: + deb_verbose("GPIO_GET_DIR\n"); -+ ret = chip->get_direction(chip, kbuf->offset); ++ ret_int = chip->get_direction(chip, kbuf->offset); + goto retval; + break; + case GPIO_SET_IN: + deb_verbose("GPIO_SET_IN\n"); -+ ret = chip->direction_input(chip, kbuf->offset); ++ ret_int = chip->direction_input(chip, kbuf->offset); + goto retval; + break; + case GPIO_SET_OUT: + deb_verbose("GPIO_SET_OUT\n"); -+ ret = chip->direction_output(chip, kbuf->offset, kbuf->level); ++ ret_int = chip->direction_output(chip, kbuf->offset, kbuf->level); + goto retval; + break; + case GPIO_GET: + deb_verbose("GPIO_GET\n"); -+ ret = chip->get(chip, kbuf->offset); ++ ret_int = chip->get(chip, kbuf->offset); + goto retval; + break; + case GPIO_SET: @@ -1392,12 +1463,12 @@ index 0000000..b26862b + break; + case GPIO_TIMESTAMP_CTRL: + deb_verbose("GPIO_TIMESTAMP_CTRL\n"); -+ ret = chip->timestamp_control(chip, kbuf->offset, kbuf->level); // mapping level onto enable ++ ret_int = chip->timestamp_control(chip, kbuf->offset, kbuf->level); // mapping level onto enable + goto retval; + break; + case GPIO_TIMESTAMP_READ: + deb_verbose("GPIO_TIMESTAMP_READ\n"); -+ ret = chip->timestamp_read(chip, kbuf->offset, (u64 *)buffer_pos); // timestamp is u64, return value as pointer ++ ret = chip->timestamp_read(chip, kbuf->offset, (uint64_t *)buffer_pos); // timestamp is u64, return value as pointer + if(ret) { + pr_err("GPIO_TIMESTAMP_READ error\n"); + goto end; @@ -1409,17 +1480,29 @@ index 0000000..b26862b + deb_verbose("GPIO_SUSPEND_CONF\n"); + if(!kbuf_ext) { + pr_err("Parameter error in GPIO_SUSPEND_CONF\n"); -+ return -EINVAL; ++ len = -EINVAL; ++ goto exit; + } -+ ret = chip->suspend_configure(chip, kbuf->offset, kbuf_ext->dflags); ++ ret_int = chip->suspend_configure(chip, kbuf->offset, kbuf_ext->dflags); + goto retval; + break; + case GPIO_ADD_PINRANGES: + deb_verbose("GPIO_ADD_PINRANGES\n"); -+ ret = chip->add_pin_ranges(chip); ++ ret_int = chip->add_pin_ranges(chip); + goto retval; + break; -+ }; ++ case TEGRA_186_GETBASE: ++ deb_verbose("TEGRA_186_GETBASE\n"); ++ kbuf_getbase = (void *)kbuf; ++ ret_ptr = tegra186_gpio_get_base_execute(kbuf_getbase->chipnum, kbuf_getbase->pin); ++ goto retptr; // 64 bit? ++ break; ++ default: ++ pr_err("GPIO, Unknown passthough signal\n"); ++ len = -EPERM; ++ goto exit; ++ break; ++ } + + /* ioctl signals use flieops because it relises on the standard gpio chardevs + static const struct file_operations gpio_fileops = { @@ -1436,10 +1519,10 @@ index 0000000..b26862b + }; + */ + -+ /* ++ /* ioctl is exluded from this version + if(kbuf_ext) { + switch (kbuf->signal) { -+*/ ++ */ + /* commands to ioctl below (the std gpio chardev) + * not fully implemented + * linehandle_create -- when userspace requests output (called by gpio_ioctl) -- bypasses the chardev @@ -1457,21 +1540,21 @@ index 0000000..b26862b + file = filp_open(tegra_chiplabel[kbuf->chipnum], O_RDWR, 0); + if (IS_ERR(file)) { + pr_err("GPIO, failed to open chardev for chip %s: %ld", tegra_chiplabel[kbuf->chipnum], PTR_ERR(file)); -+ kfree(kbuf); -+ return -ENOENT; ++ len = -ENOENT; ++ goto exit; + } + // note: inode and file are static variables + inode = file->f_path.dentry->d_inode; + // defined as: static int gpio_chrdev_open(struct inode *inode, struct file *file) -+ ret = file->f_op->open(inode, file); ++ ret_int = file->f_op->open(inode, file); + goto retval; + break; + case GPIO_CHARDEV_IOCTL: // .unlocked_ioctl = gpio_ioctl + // user space triggers gpio_ioctl -- it is .unlocked_ioctl on the chardev + if( !file ) { + pr_err("GPIO, chardev file was expected to be open\n"); -+ kfree(kbuf); -+ return -ENOENT; ++ len = -ENOENT; ++ goto exit; + } + // defined as: static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + ret_l = file->f_op->unlocked_ioctl(file, kbuf->cmd, kbuf_ext->arg); // arg is pointer data which should have been copied from userspace @@ -1480,74 +1563,86 @@ index 0000000..b26862b + case GPIO_CHARDEV_RELEASE: // .release = gpio_chrdev_release + if( !file ) { + pr_err("GPIO, chardev file was expected to be open\n"); -+ kfree(kbuf); -+ return -ENOENT; ++ len = -ENOENT; ++ goto exit; + } + // defined as: static int gpio_chrdev_release(struct inode *inode, struct file *file) -+ ret = file->f_op->release(inode, file); ++ ret_int = file->f_op->release(inode, file); + goto retval; + break; + case GPIO_CHARDEV_POLL: // .poll = lineinfo_watch_poll + if( !file ) { + pr_err("GPIO, chardev file was expected to be open\n"); -+ kfree(kbuf); -+ return -ENOENT; -+ }) ++ len = -ENOENT; ++ goto exit; ++ } + // defined as: static __poll_t lineinfo_watch_poll(struct file *file, struct poll_table_struct *pollt) -+ ret = file->f_op->poll(file, kbuf_ext->poll); // TODO arg is pointer data which should have been copied ++ ret_int = file->f_op->poll(file, kbuf_ext->poll); // TODO arg is pointer data which should have been copied + goto retval; // __poll_t is of size unsigned int + break; + case GPIO_CHARDEV_READ: // .read = lineinfo_watch_read + if( !file ) { + pr_err("GPIO, chardev file was expected to be open\n"); -+ kfree(kbuf); -+ return -ENOENT; ++ len = -ENOENT; ++ goto exit; + } + // defined as: static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, size_t count, loff_t *off) + ret = file->f_op->read(file, buffer_pos, kbuf_ext->count, NULL); // + if (ret) { + pr_err("Reading lineinfo returned zero\n"); -+ kfree(kbuf); -+ return -EFAULT; ++ len = -EFAULT; ++ goto exit; + } -+ return -ENXIO; ++ break; + case GPIO_CHARDEV_OWNER: // .owner = THIS_MODULE + if (copy_to_user(buffer_pos, file->f_op->owner->name, strlen(file->f_op->owner->name)+1)) { + pr_err("GPIO, copying user return value failed\n"); -+ kfree(kbuf); -+ return -EFAULT; ++ len = -EFAULT; ++ goto exit; + } + break; + default: -+ pr_err("GPIO, Illegal proxy signal type\n"); -+ kfree(kbuf); -+ return -EPERM; ++ pr_err("GPIO, Unknown passthough signal\n"); ++ len = -EPERM; ++ goto exit; + break; + }; + }; ++ */ + + goto end; + -+ retlong: -+ if ( copy_to_user(buffer_pos, &ret_l, sizeof(ret_l)) ) { -+ pr_err("GPIO, copying int user return value failed\n"); -+ kfree(kbuf); -+ return -EFAULT; -+ }; -+ */ -+ ++ retptr: ++ return_size = sizeof(ret_ptr); ++ memcpy(return_buffer, &ret_ptr, return_size); ++ deb_verbose("retval pointer (host): 0x%p, 0x%016llX", ret_ptr, (uint64_t)ret_ptr); + goto end; + + retval: -+ *return_value = ret; -+ deb_verbose("retval (host): 0x%X", ret); -+ if ( copy_to_user((void *)buffer, &ret, sizeof(ret)) ) { -+ pr_err("GPIO, copying unsigned int user return value failed\n"); -+ kfree(kbuf); -+ return -EFAULT; -+ }; ++ return_size = sizeof(ret_int); ++ memcpy(return_buffer, &ret_int, return_size); ++ deb_verbose("retval int (host): 0x%X", ret); + + end: ++ *offset = RETURN_OFF; ++ len = *offset; ++ len += sizeof(return_buffer); ++ ++ if (MEM_SIZE >= return_size + RETURN_OFF) { ++ if ( (ret = copy_to_user( (char *)buffer_pos, return_buffer, sizeof(return_buffer))) ) { ++ pr_err("GPIO, copying user return value failed: 0x%08X\n", ret); ++ len = -EFAULT; ++ goto exit; ++ } ++ len += sizeof(return_buffer); ++ } else { ++ len = -EINVAL; // Buffer too small ++ goto exit; ++ } ++ // deb_verbose("retval copied to buffer (host): 0x%016llX", *(uint64_t *)return_buffer); ++ // hexDump(DEVICE_NAME, "Chardev (host write) dump buffer", return_buffer, MEM_SIZE); ++ ++ exit: + kfree(kbuf); + return len; // return length of read data +} @@ -1581,6 +1676,8 @@ index 0000000..b26862b + deb_info("GPIO gpio host proxy driver registered successfully\n"); + } + ++ memset(return_buffer, 0, RET_SIZE); ++ + return ret; +} + @@ -1594,10 +1691,10 @@ index 0000000..b26862b +module_exit(gpio_host_proxy_exit); diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.h b/drivers/gpio-host-proxy/gpio-host-proxy.h new file mode 100644 -index 0000000..55cdbaa +index 0000000..3aad6c5 --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.h -@@ -0,0 +1,115 @@ +@@ -0,0 +1,131 @@ +#ifndef __GPIO_HOST_PROXY__H__ +#define __GPIO_HOST_PROXY__H__ + @@ -1619,7 +1716,10 @@ index 0000000..55cdbaa + +// size of qemu iomem. +// Note: Must be synchronized with value in qemu (hw/misc/nvidia_gpio_guest.c) -+#define MEM_SIZE 0x80 ++#define MEM_SIZE 0x18 // mem size in bytes is three 64 bit words ++#define RETURN_OFF 0x10 // offset for return value -- two 64 bit words ++// #define RETURN_OFF 0 ++#define MAX_CHIP 2 + +/* values to be used as "signal" values in struct tegra_gpio_pt */ + @@ -1640,6 +1740,8 @@ index 0000000..55cdbaa +#define GPIO_SUSPEND_CONF 'S' // suspend configure +#define GPIO_ADD_PINRANGES 'P' // add_pin_ranges + ++#define TEGRA_186_GETBASE 'B' // pastrhrough of tegra186_gpio_get_base in gpio_tegra186.c ++ +// helpers to identify chip +#define TEGRA_GPIO_CHIP 0 // tegra-gpio gpio_main_chip +#define TEGRA_GPIO_AON_CHIP 1 // tegra-gpio-aon gpio_aon_chip @@ -1652,9 +1754,9 @@ index 0000000..55cdbaa +#define GPIO_READL '<' +#define GPIO_WRITEL '>' + -+#define RWL_STD '0' // general readl/writeL -+#define RWL_RAW '1' // __raw assembler version of readl/writel -+#define RWL_RELAXED '2' // __relaxed assembler version of readl/writel ++#define RWL_STD '8' // general readl/writeL ++#define RWL_RAW '9' // __raw assembler version of readl/writel ++#define RWL_RELAXED '0' // __relaxed assembler version of readl/writel + // +#define GPIO_CHARDEV_OPEN '1' // .open = gpio_chrdev_open +#define GPIO_CHARDEV_IOCTL '2' // .unlocked_ioctl = gpio_ioctl -- handles IO operation, get linehandle, set direction @@ -1665,11 +1767,13 @@ index 0000000..55cdbaa +#define GPIO_CHARDEV_RELEASE '7' // .release = gpio_chrdev_release + + -+// Note this extern is also in gpio-proxy.h in the kernel source tree (this proxy code might be in an overlay) ++// Note these externs are also in gpio-proxy.h in the kernel source tree (this proxy code might be in an overlay) ++// make sure values are synchronised (todo: could be fixed) +extern const unsigned char rwl_std_type; +extern const unsigned char rwl_raw_type; +extern const unsigned char rwl_relaxed_type; + ++ +// sizeof is rounded to even 64 bit passhtough writes -- no need to optimise size further on an aarch64 +struct tegra_readl_writel { + unsigned char length; // shift right one bit, most LSB is ignored @@ -1680,16 +1784,33 @@ index 0000000..55cdbaa + void * address; +}; + ++_Static_assert( sizeof(struct tegra_readl_writel) == 16, ++ "tegra_readl_writel size is not 16 bytes." ); ++ +// struct __attribute__((packed)) tegra_gpio_pt { +struct tegra_gpio_pt { + unsigned char chipnum; // lowest bit is number of gpio chip (gpiochip0 or gpiochip1), top 7 bits are message length + unsigned char signal; // defines operation -+ unsigned char level; // level to set gpio pin to ++ unsigned char level; // level to set gpio pin to // note: level and offset must be consecutive because we overload that memory space with an uint for 'pin' + unsigned char offset; // address offset for gpio pin + // u32 cmd; // gpio_ioctl command + // tegra_gpio_pt_extended p2; // extended parameters -- in second word of struct +}; + ++_Static_assert( sizeof(struct tegra_gpio_pt) == 4, ++ "tegra_gpio_pt size is not 4 bytes." ); ++ ++// struct __attribute__((packed)) tegra_gpio_pt { ++struct tegra_getbase_pt { ++ unsigned char chipnum; // lowest bit is number of gpio chip (gpiochip0 or gpiochip1), top 7 bits are message length ++ unsigned char signal; // defines operation ++ unsigned char pad[2]; ++ unsigned int pin; // chosen pins to process ++}; ++ ++_Static_assert( sizeof(struct tegra_getbase_pt) == 8, ++ "tegra_getbase_pt size is not 4 bytes." ); ++ +union extended { + // int level; // pin level to be set + unsigned long config; // pin configuration @@ -1702,14 +1823,6 @@ index 0000000..55cdbaa + +typedef union extended tegra_gpio_pt_extended; + -+#define MAX_CHIP 2 -+ -+_Static_assert( sizeof(struct tegra_readl_writel) == 16, -+ "tegra_readl_writel size is not 16 bytes." ); -+ -+_Static_assert( sizeof(struct tegra_gpio_pt) == 4, -+ "tegra_gpio_pt size is not 4 bytes." ); -+ +_Static_assert( sizeof(tegra_gpio_pt_extended) == 8, + "tegra_gpio_pt_extended size is not 8 bytes." ); +#endif From 93d5c8a2b7e951880306cf95aebe2f840022d736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Fri, 2 Aug 2024 08:15:02 +0000 Subject: [PATCH 24/26] kernel has working passthrough for GPIO - gpio-virt commit 71192f60bd8b8f6be4d529c49b7c33529e21a1e2 - kernel 5.10 commit fb9220ac23c9892ec6433f0e839b8f919511a927 - qemu in ghaf is not yet updated - use qemu 8.1.2 patch in commit 95d5cdb19713369a9dceab53659628292d9ff884 - precompiled into generation 299 (on dev system) --- .../patches/0003-gpio-virt-kernel.patch | 105 ++-- .../patches/0004-gpio-virt-drivers.patch | 581 +++++++++++------- .../microvm/simple-chardev-test.sh | 152 ++--- .../microvm/simple-gpiod-test.sh | 45 ++ 4 files changed, 525 insertions(+), 358 deletions(-) create mode 100644 modules/microvm/virtualization/microvm/simple-gpiod-test.sh diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch index 34bec92ab..e5eeb58cf 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch @@ -351,7 +351,7 @@ index f66fc17faee4..9706a77fe5a5 100644 } subsys_initcall(tegra_gpio_init); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c -index 5e57824b283e..2743beecfa2f 100644 +index 5e57824b283e..43f83675ec3f 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -20,6 +20,46 @@ @@ -513,7 +513,7 @@ index 5e57824b283e..2743beecfa2f 100644 port = tegra186_gpio_get_port(gpio, &pin); if (!port) return NULL; -@@ -447,12 +499,33 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, +@@ -447,12 +499,34 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, return gpio->base + offset + pin * 0x20; } @@ -527,6 +527,7 @@ index 5e57824b283e..2743beecfa2f 100644 + } +EXPORT_SYMBOL_GPL(tegra186_gpio_get_base_execute); + ++//checks if we are on host or guest. Guest calls the redidrec function +static inline void __iomem *tegra186_gpio_get_base_x(struct tegra_gpio *tgpio, unsigned int pin) { + if(kernel_is_on_guest) { + return tegra186_gpio_get_base_redirect(tgpio->gpio.gpiodev->id, pin); @@ -547,7 +548,7 @@ index 5e57824b283e..2743beecfa2f 100644 port = tegra186_gpio_get_port(gpio, &pin); if (!port) return NULL; -@@ -466,14 +539,17 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) +@@ -466,14 +540,17 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) void __iomem *secure; u32 val; @@ -569,7 +570,7 @@ index 5e57824b283e..2743beecfa2f 100644 if ((val & (GPIO_SCR_SEC_ENABLE)) == 0) return true; -@@ -484,13 +560,16 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) +@@ -484,13 +561,16 @@ static inline bool gpio_is_accessible(struct tegra_gpio *gpio, u32 pin) return false; } @@ -587,7 +588,7 @@ index 5e57824b283e..2743beecfa2f 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -502,10 +581,12 @@ static int tegra186_gpio_get_direction(struct gpio_chip *chip, +@@ -502,10 +582,12 @@ static int tegra186_gpio_get_direction(struct gpio_chip *chip, if (value & TEGRA186_GPIO_ENABLE_CONFIG_OUT) return GPIO_LINE_DIRECTION_OUT; @@ -601,7 +602,7 @@ index 5e57824b283e..2743beecfa2f 100644 unsigned int offset) { struct tegra_gpio *gpio = gpiochip_get_data(chip); -@@ -513,6 +594,8 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, +@@ -513,6 +595,8 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, u32 value; int ret = 0; @@ -610,7 +611,7 @@ index 5e57824b283e..2743beecfa2f 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -536,7 +619,8 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, +@@ -536,7 +620,8 @@ static int tegra186_gpio_direction_input(struct gpio_chip *chip, return ret; } @@ -620,7 +621,7 @@ index 5e57824b283e..2743beecfa2f 100644 unsigned int offset, int level) { struct tegra_gpio *gpio = gpiochip_get_data(chip); -@@ -544,6 +628,8 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, +@@ -544,6 +629,8 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, u32 value; int ret = 0; @@ -629,7 +630,7 @@ index 5e57824b283e..2743beecfa2f 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -571,6 +657,7 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, +@@ -571,6 +658,7 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, return ret; } @@ -637,7 +638,7 @@ index 5e57824b283e..2743beecfa2f 100644 static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, enum gpiod_flags dflags) { -@@ -578,6 +665,8 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, +@@ -578,6 +666,8 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, struct tegra_gpio_saved_register *regs; void __iomem *base; @@ -646,7 +647,7 @@ index 5e57824b283e..2743beecfa2f 100644 if (!gpio_is_accessible(gpio, offset)) return -EPERM; -@@ -598,6 +687,7 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, +@@ -598,6 +688,7 @@ static int tegra_gpio_suspend_configure(struct gpio_chip *chip, unsigned offset, return tegra186_gpio_direction_input(chip, offset); } @@ -654,7 +655,7 @@ index 5e57824b283e..2743beecfa2f 100644 static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, int enable) { -@@ -606,6 +696,8 @@ static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, +@@ -606,6 +697,8 @@ static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, int value; int ret; @@ -663,7 +664,7 @@ index 5e57824b283e..2743beecfa2f 100644 base = tegra186_gpio_get_base(gpio, offset); if (WARN_ON(base == NULL)) return -EINVAL; -@@ -613,7 +705,7 @@ static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, +@@ -613,7 +706,7 @@ static int tegra_gpio_timestamp_control(struct gpio_chip *chip, unsigned offset, if (gpio->use_timestamp) { value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); value |= TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMP_FUNC; @@ -672,7 +673,7 @@ index 5e57824b283e..2743beecfa2f 100644 if (enable) ret = tegra_gte_enable_ts(gpio, offset); else -@@ -630,6 +722,8 @@ static int tegra_gpio_timestamp_read(struct gpio_chip *chip, unsigned offset, +@@ -630,6 +723,8 @@ static int tegra_gpio_timestamp_read(struct gpio_chip *chip, unsigned offset, struct tegra_gpio *tgi = gpiochip_get_data(chip); int ret; @@ -681,7 +682,7 @@ index 5e57824b283e..2743beecfa2f 100644 if (tgi->use_timestamp) { *ts = tegra_gte_read_fifo(tgi, offset); ret = 0; -@@ -639,12 +733,15 @@ static int tegra_gpio_timestamp_read(struct gpio_chip *chip, unsigned offset, +@@ -639,12 +734,15 @@ static int tegra_gpio_timestamp_read(struct gpio_chip *chip, unsigned offset, return ret; } @@ -697,7 +698,7 @@ index 5e57824b283e..2743beecfa2f 100644 base = tegra186_gpio_get_base(gpio, offset); if (WARN_ON(base == NULL)) return -ENODEV; -@@ -658,17 +755,20 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) +@@ -658,17 +756,20 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) return value & BIT(0); } @@ -721,7 +722,7 @@ index 5e57824b283e..2743beecfa2f 100644 if (WARN_ON(base == NULL)) return; -@@ -681,6 +781,17 @@ static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, +@@ -681,6 +782,17 @@ static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); } @@ -739,7 +740,7 @@ index 5e57824b283e..2743beecfa2f 100644 static int tegra186_gpio_set_config(struct gpio_chip *chip, unsigned int offset, unsigned long config) -@@ -689,6 +800,8 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, +@@ -689,6 +801,8 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, u32 debounce, value; void __iomem *base; @@ -748,7 +749,7 @@ index 5e57824b283e..2743beecfa2f 100644 base = tegra186_gpio_get_base(gpio, offset); if (base == NULL) return -ENXIO; -@@ -717,6 +830,7 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, +@@ -717,6 +831,7 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip, return 0; } @@ -756,7 +757,7 @@ index 5e57824b283e..2743beecfa2f 100644 static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) { struct tegra_gpio *gpio = gpiochip_get_data(chip); -@@ -725,6 +839,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) +@@ -725,6 +840,8 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) unsigned int i, j; int err; @@ -765,7 +766,7 @@ index 5e57824b283e..2743beecfa2f 100644 if (!gpio->soc->pinmux || gpio->soc->num_pin_ranges == 0) return 0; -@@ -761,6 +877,7 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) +@@ -761,6 +878,7 @@ static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip) return 0; } @@ -773,7 +774,7 @@ index 5e57824b283e..2743beecfa2f 100644 static int tegra186_gpio_of_xlate(struct gpio_chip *chip, const struct of_phandle_args *spec, u32 *flags) -@@ -768,6 +885,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, +@@ -768,6 +886,8 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, struct tegra_gpio *gpio = gpiochip_get_data(chip); unsigned int port, pin, i, offset = 0; @@ -782,7 +783,7 @@ index 5e57824b283e..2743beecfa2f 100644 if (WARN_ON(chip->of_gpio_n_cells < 2)) return -EINVAL; -@@ -793,19 +912,21 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, +@@ -793,19 +913,21 @@ static int tegra186_gpio_of_xlate(struct gpio_chip *chip, #define to_tegra_gpio(x) container_of((x), struct tegra_gpio, gpio) @@ -806,7 +807,7 @@ index 5e57824b283e..2743beecfa2f 100644 static void tegra186_irq_mask(struct irq_data *data) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); -@@ -813,15 +934,16 @@ static void tegra186_irq_mask(struct irq_data *data) +@@ -813,15 +935,16 @@ static void tegra186_irq_mask(struct irq_data *data) void __iomem *base; u32 value; @@ -826,7 +827,7 @@ index 5e57824b283e..2743beecfa2f 100644 static void tegra186_irq_unmask(struct irq_data *data) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); -@@ -829,15 +951,16 @@ static void tegra186_irq_unmask(struct irq_data *data) +@@ -829,15 +952,16 @@ static void tegra186_irq_unmask(struct irq_data *data) void __iomem *base; u32 value; @@ -846,7 +847,7 @@ index 5e57824b283e..2743beecfa2f 100644 static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); -@@ -845,11 +968,11 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) +@@ -845,11 +969,11 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) void __iomem *base; u32 value; @@ -860,7 +861,7 @@ index 5e57824b283e..2743beecfa2f 100644 value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK; value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL; -@@ -883,7 +1006,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) +@@ -883,7 +1007,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) return -EINVAL; } @@ -869,7 +870,7 @@ index 5e57824b283e..2743beecfa2f 100644 if ((type & IRQ_TYPE_EDGE_BOTH) == 0) irq_set_handler_locked(data, handle_level_irq); -@@ -904,6 +1027,7 @@ static int tegra186_irq_set_wake(struct irq_data *data, unsigned int on) +@@ -904,6 +1028,7 @@ static int tegra186_irq_set_wake(struct irq_data *data, unsigned int on) return 0; } @@ -877,7 +878,7 @@ index 5e57824b283e..2743beecfa2f 100644 static void tegra186_gpio_irq(struct irq_desc *desc) { struct tegra_gpio *gpio = irq_desc_get_handler_data(desc); -@@ -930,7 +1054,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) +@@ -930,7 +1055,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) if (j == gpio->num_irqs_per_bank) goto skip; @@ -886,7 +887,7 @@ index 5e57824b283e..2743beecfa2f 100644 for_each_set_bit(pin, &value, port->pins) { irq = irq_find_mapping(domain, offset + pin); -@@ -1031,12 +1155,15 @@ static const struct of_device_id tegra186_pmc_of_match[] = { +@@ -1031,12 +1156,15 @@ static const struct of_device_id tegra186_pmc_of_match[] = { { /* sentinel */ } }; @@ -902,7 +903,7 @@ index 5e57824b283e..2743beecfa2f 100644 for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; unsigned int offset, p = port->port; -@@ -1044,7 +1171,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1044,7 +1172,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) base = gpio->secure + port->bank * 0x1000 + 0x800; @@ -911,7 +912,7 @@ index 5e57824b283e..2743beecfa2f 100644 /* * For controllers that haven't been locked down yet, make -@@ -1058,7 +1185,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1058,7 +1186,7 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) */ for (j = 0; j < gpio->num_irqs_per_bank; j++) { dev_dbg(dev, "programming default interrupt routing for port %s\n", @@ -920,7 +921,7 @@ index 5e57824b283e..2743beecfa2f 100644 offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j); -@@ -1073,9 +1200,9 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +@@ -1073,9 +1201,9 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) */ if (j == 0) { @@ -932,7 +933,7 @@ index 5e57824b283e..2743beecfa2f 100644 } } } -@@ -1107,6 +1234,232 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +@@ -1107,6 +1235,232 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) return -EINVAL; } @@ -1002,9 +1003,9 @@ index 5e57824b283e..2743beecfa2f 100644 + gpio->gpio.set = tegra186_gpio_set_redirect; + // gpio->gpio.set_multiple = N/A; + gpio->gpio.set_config = tegra186_gpio_set_config_redirect; -+ // gpio->gpio.set_config = tegra186_gpio_get_port_redirect; // not in struct -+ // gpio->gpio.set_config = tegra186_gpio_get_base_redirect; // not in struct -+ // gpio->gpio.set_config = tegra186_gpio_get_secure_redirect; // not in struct ++ // ??? = tegra186_gpio_get_port_redirect; // not in struct ++ // ??? = tegra186_gpio_get_base_redirect; // not in struct ++ // ??? = tegra186_gpio_get_secure_redirect; // not in struct + gpio->gpio.timestamp_control = tegra_gpio_timestamp_control_redirect; + gpio->gpio.timestamp_read = tegra_gpio_timestamp_read_redirect; + gpio->gpio.suspend_configure = tegra_gpio_suspend_configure_redirect; @@ -1132,7 +1133,7 @@ index 5e57824b283e..2743beecfa2f 100644 + } + if(id & ~0x00000001) { + pr_err("GPIO, *ERROR* Illegal chip number (%d)", id); -+ return 0; ++ return NULL; + } + else + return &tegra_gpio_hosts[id]->gpio; @@ -1152,7 +1153,7 @@ index 5e57824b283e..2743beecfa2f 100644 + */ + if(id & ~0x00000001) { + pr_err("GPIO, *ERROR* Illegal chip number (%d)", id); -+ return 0; ++ return NULL; + } + else + return tegra_gpio_hosts[id]; @@ -1165,7 +1166,7 @@ index 5e57824b283e..2743beecfa2f 100644 static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; -@@ -1120,17 +1473,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1120,17 +1474,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) int value; void __iomem *base; @@ -1210,7 +1211,7 @@ index 5e57824b283e..2743beecfa2f 100644 /* count the number of banks in the controller */ for (i = 0; i < gpio->soc->num_ports; i++) -@@ -1139,11 +1515,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1139,11 +1516,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->num_banks++; @@ -1227,7 +1228,7 @@ index 5e57824b283e..2743beecfa2f 100644 sizeof(*gpio->gpio_rval), GFP_KERNEL); if (!gpio->gpio_rval) return -ENOMEM; -@@ -1154,9 +1534,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1154,9 +1535,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -EINVAL; } @@ -1240,7 +1241,7 @@ index 5e57824b283e..2743beecfa2f 100644 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gte"); if (!res) { dev_err(&pdev->dev, "Missing gte MEM resource\n"); -@@ -1172,14 +1555,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1172,14 +1556,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) } err = platform_irq_count(pdev); @@ -1264,7 +1265,7 @@ index 5e57824b283e..2743beecfa2f 100644 gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), GFP_KERNEL); -@@ -1188,27 +1577,43 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1188,27 +1578,43 @@ static int tegra186_gpio_probe(struct platform_device *pdev) for (i = 0; i < gpio->num_irq; i++) { err = platform_get_irq(pdev, i); @@ -1324,7 +1325,7 @@ index 5e57824b283e..2743beecfa2f 100644 for (i = 0; i < gpio->soc->num_ports; i++) gpio->gpio.ngpio += gpio->soc->ports[i].pins; -@@ -1229,6 +1634,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1229,6 +1635,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -ENOMEM; names[offset + j] = name; @@ -1332,7 +1333,7 @@ index 5e57824b283e..2743beecfa2f 100644 } offset += port->pins; -@@ -1260,6 +1666,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1260,6 +1667,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parent_handler_data = gpio; irq->num_parents = gpio->num_irq; @@ -1341,7 +1342,7 @@ index 5e57824b283e..2743beecfa2f 100644 /* * To simplify things, use a single interrupt per bank for now. Some -@@ -1284,7 +1692,18 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1284,7 +1693,18 @@ static int tegra186_gpio_probe(struct platform_device *pdev) } if (gpio->soc->num_irqs_per_bank > 1) @@ -1360,7 +1361,7 @@ index 5e57824b283e..2743beecfa2f 100644 np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (!of_device_is_available(np)) -@@ -1315,9 +1734,31 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1315,9 +1735,31 @@ static int tegra186_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, gpio); @@ -1395,7 +1396,7 @@ index 5e57824b283e..2743beecfa2f 100644 if (gpio->soc->is_hw_ts_sup) { for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) { -@@ -1325,28 +1766,29 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1325,28 +1767,29 @@ static int tegra186_gpio_probe(struct platform_device *pdev) &gpio->soc->ports[i]; for (j = 0; j < port->pins; j++) { @@ -1433,7 +1434,7 @@ index 5e57824b283e..2743beecfa2f 100644 static int tegra_gpio_resume_early(struct device *dev) { struct tegra_gpio *gpio = dev_get_drvdata(dev); -@@ -1355,7 +1797,7 @@ static int tegra_gpio_resume_early(struct device *dev) +@@ -1355,7 +1798,7 @@ static int tegra_gpio_resume_early(struct device *dev) void __iomem *base; int i; @@ -1442,7 +1443,7 @@ index 5e57824b283e..2743beecfa2f 100644 if (WARN_ON(base == NULL)) return -EINVAL; -@@ -1366,9 +1808,9 @@ static int tegra_gpio_resume_early(struct device *dev) +@@ -1366,9 +1809,9 @@ static int tegra_gpio_resume_early(struct device *dev) regs->restore_needed = false; @@ -1455,7 +1456,7 @@ index 5e57824b283e..2743beecfa2f 100644 } return 0; -@@ -1723,7 +2165,9 @@ static struct platform_driver tegra186_gpio_driver = { +@@ -1723,7 +2166,9 @@ static struct platform_driver tegra186_gpio_driver = { .probe = tegra186_gpio_probe, .remove = tegra186_gpio_remove, }; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index 73c5071fb..edeb89aa8 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -15,7 +15,7 @@ index dcecc9f6e33f..ed7d58d68ba6 100644 endmenu diff --git a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile 2024-05-02 12:06:58.097355696 +0000 -+++ b/drivers/Makefile 2024-07-24 12:47:03.482141344 +0000 ++++ b/drivers/Makefile 2024-08-01 14:32:02.521803787 +0000 @@ -193,0 +194,5 @@ +# +# @@ -48,10 +48,10 @@ index 0000000..2580e02 +obj-$(CONFIG_TEGRA_GPIO_GUEST_PROXY) += gpio-guest-proxy.o diff --git a/drivers/gpio-guest-proxy/gpio-guest-proxy.c b/drivers/gpio-guest-proxy/gpio-guest-proxy.c new file mode 100644 -index 0000000..8696a99 +index 0000000..6fbc960 --- /dev/null +++ b/drivers/gpio-guest-proxy/gpio-guest-proxy.c -@@ -0,0 +1,851 @@ +@@ -0,0 +1,909 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -125,7 +125,8 @@ index 0000000..8696a99 + +#define RET_SIZE 8 // should be sizeof(uint64_t) +// static char return_buffer[MEM_SIZE]; // using the same size as the input buffer -+static char return_buffer[RET_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0}; ++static uint64_t return_value = 0; ++static char *return_buffer = (char *)&return_value; +static unsigned int return_size = 0; + +_Static_assert(sizeof(uint64_t) == RET_SIZE, "return size assertion for RET_SIZE failed"); @@ -138,7 +139,7 @@ index 0000000..8696a99 + +/* guest_chardev_transfer + * a helper function to transfer between guest and host using /dev/gpio-host -+ * mgs: is a pointer to data -- in practice tegra_gpio_pt and tegra_gpio_pt_extended structs ++ * mgs: is a pointer to data -- in practice tegra_gpio_pt and tegra_gpio_pt_ext structs + * generic return: poiter to return data, may be NULL if we do not expect a return value + */ +inline void guest_chardev_transfer(void *msg, char msg_len, void *generic_return, int ret_len) @@ -152,7 +153,7 @@ index 0000000..8696a99 + } + + deb_verbose("PT transfer signal is: %c", *((char *)msg + 1)); -+ hexDump(DEVICE_NAME, "PT transfer (message to host)", msg, msg_len); ++ hexDump(DEVICE_NAME, "GPIO: PT transfer (message from guest)", msg, msg_len); + + // Execute the request by copying to io memory + memcpy_toio(mem_iova, msg, msg_len); @@ -165,19 +166,19 @@ index 0000000..8696a99 + // hexDump(DEVICE_NAME, "GPIO: PT dump mem_iova", (char *)mem_iova, MEM_SIZE); + // deb_verbose("ret_len = %d\n", ret_len); + memcpy_fromio(generic_return, (void *)((char *)mem_iova + RETURN_OFF), ret_len); // 32 bits except for tegra186_gpio_get_base_redirect 64 bits -+ hexDump(DEVICE_NAME, "GPIO: PT transfer (retrieved retval from host)", generic_return, ret_len ); } ++ hexDump(DEVICE_NAME, "GPIO: PT transfer (retrieved retval to guest)", generic_return, ret_len ); } +} + -+void __iomem *tegra186_gpio_get_base_redirect(unsigned char id, unsigned int pin) { -+ void __iomem *ret_ptr = (void __iomem *)0x01234567ABADFACE; -+ struct tegra_getbase_pt msg; ++void *tegra186_gpio_get_base_redirect(unsigned char id, unsigned int pin) { ++ void *ret_ptr = (void *)0x01234567ABADFACE; // simulating a pointer with a uint64_t ++ struct tegra_gpio_pt_ext msg; + -+ msg.signal = TEGRA_186_GETBASE; -+ msg.chipnum = id; -+ msg.pin = pin; ++ msg.base.signal = TEGRA_186_GETBASE; ++ msg.base.chipnum = id; ++ msg.ext.pin = pin; + + guest_chardev_transfer(&msg, sizeof(msg), &ret_ptr, sizeof(ret_ptr)); -+ deb_verbose(" 0x%p", ret_ptr); ++ deb_verbose(" id = %d, ptr = 0x%p / 0x%016llX", id, ret_ptr, (uint64_t)ret_ptr); + return ret_ptr; +} + @@ -193,7 +194,7 @@ index 0000000..8696a99 + rwlmsg.value = 0; // value field is not used for readl + + guest_chardev_transfer(&rwlmsg, sizeof(rwlmsg), &ret, sizeof(ret)); -+ deb_verbose(" 0x%X", ret); ++ deb_verbose("addr = 0x%p / 0x%016llX, ret = 0x%X", addr, (uint64_t)addr, ret); + return ret; +} +EXPORT_SYMBOL_GPL(readl_redirect); @@ -209,6 +210,7 @@ index 0000000..8696a99 + rwlmsg.value = value; + + guest_chardev_transfer(&rwlmsg, sizeof(rwlmsg), NULL, 0); ++ deb_verbose("addr = 0x%p / 0x%016llX", addr, (uint64_t)addr); +} +EXPORT_SYMBOL_GPL(writel_redirect); + @@ -323,12 +325,13 @@ index 0000000..8696a99 + unsigned int offset, + unsigned long config) { + int ret = 0; -+ struct tegra_gpio_pt msg; ++ struct tegra_gpio_pt_ext msg; + -+ msg.signal = GPIO_CONFIG; -+ msg.chipnum = chip->gpiodev->id; -+ msg.level = 0; -+ msg.offset = offset; ++ msg.base.signal = GPIO_CONFIG; ++ msg.base.chipnum = chip->gpiodev->id; ++ msg.base.level = 0; ++ msg.base.offset = offset; ++ msg.ext.config = config; + + guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); + return ret; @@ -350,7 +353,13 @@ index 0000000..8696a99 + +int tegra_gpio_timestamp_read_redirect(struct gpio_chip *chip, unsigned offset, + u64 *ts) { -+ int ret = 0; ++ /* TODO include 'ret' in the return values (requires change in qemu) ++ struct ret_ts { ++ uint64_t timestamp; ++ int ret = 0; ++ } */ ++ ++ int ret = 0; + struct tegra_gpio_pt msg; + + msg.signal = GPIO_TIMESTAMP_READ; @@ -358,19 +367,28 @@ index 0000000..8696a99 + msg.level = 0; + msg.offset = offset; + -+ guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); ++ guest_chardev_transfer(&msg, sizeof(msg), ts, sizeof(*ts)); ++ // *ts = ret_ts.timestamp ++ // return ret_ts.ret; ++ if(*ts == -EOPNOTSUPP) ret = (int)*ts; // possible hack to use *ts for error reporting ++ ++ // a hack to read extra return att offset -1 ++ // memcpy_fromio(&ret, (void *)((char *)mem_iova + RETURN_OFF - 1), sizeof(ret)); // 32 bits except for tegra186_gpio_get_base_redirect 64 bits ++ ++ deb_verbose("timestamp: 0x%016llX\n", *ts); + return ret; +} + +int tegra_gpio_suspend_configure_redirect(struct gpio_chip *chip, unsigned offset, + enum gpiod_flags dflags) { + int ret = 0; -+ struct tegra_gpio_pt msg; ++ struct tegra_gpio_pt_ext msg; + -+ msg.signal = GPIO_SUSPEND_CONF; -+ msg.chipnum = chip->gpiodev->id; -+ msg.level = 0; -+ msg.offset = offset; ++ msg.base.signal = GPIO_SUSPEND_CONF; ++ msg.base.chipnum = chip->gpiodev->id; ++ msg.base.level = 0; ++ msg.base.offset = offset; ++ msg.ext.dflags = dflags; + + guest_chardev_transfer(&msg, sizeof(msg), &ret, sizeof(ret)); + return ret; @@ -441,19 +459,19 @@ index 0000000..8696a99 + deb_info("installing module."); + + if(!gpio_vpa){ -+ pr_err("Failed, gpio_vpa not defined"); ++ deb_error("Failed, gpio_vpa not defined"); + return -1; + } -+ deb_info("gpio_vpa: 0x%llx", gpio_vpa); ++ deb_debug("gpio_vpa: 0x%llx", gpio_vpa); + + // Allocate a major number for the device. + major_number = register_chrdev(0, DEVICE_NAME, &fops); + if (major_number < 0) + { -+ pr_err("could not register number."); ++ deb_error("could not register number."); + return major_number; + } -+ deb_info("registered correctly with major number %d", major_number); ++ deb_debug("registered correctly with major number %d", major_number); + + // Register the device class + gpio_guest_proxy_class = class_create(THIS_MODULE, CLASS_NAME); @@ -463,7 +481,7 @@ index 0000000..8696a99 + deb_error("Failed to register device class\n"); + return PTR_ERR(gpio_guest_proxy_class); // Correct way to return an error on a pointer + } -+ deb_info("device class registered correctly\n"); ++ deb_debug("device class registered correctly\n"); + + // Register the device driver + gpio_guest_proxy_device = device_create(gpio_guest_proxy_class, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME); @@ -474,7 +492,7 @@ index 0000000..8696a99 + deb_error("Failed to create the device\n"); + return PTR_ERR(gpio_guest_proxy_device); + } -+ deb_info("device class created correctly\n"); // Made it! device was initialized ++ deb_debug("device class created correctly\n"); // Made it! device was initialized + + // map iomem + mem_iova = ioremap(gpio_vpa, MEM_SIZE); @@ -484,7 +502,7 @@ index 0000000..8696a99 + return -ENOMEM; + } + -+ deb_info("mem_iova: 0x%llx\n", (long long unsigned int)mem_iova); ++ deb_debug("mem_iova: 0x%llx\n", (long long unsigned int)mem_iova); + + is_set_up = true; + return 0; @@ -510,7 +528,7 @@ index 0000000..8696a99 + class_unregister(gpio_guest_proxy_class); // unregister the device class + class_destroy(gpio_guest_proxy_class); // remove the device class + unregister_chrdev(major_number, DEVICE_NAME); // unregister the major number -+ deb_info("Goodbye from the LKM!\n"); ++ deb_info("Goodbye from GPIO passthrough!\n"); + unregister_chrdev(major_number, DEVICE_NAME); + + is_set_up = false; @@ -540,6 +558,8 @@ index 0000000..8696a99 + return 0; +} + ++static DEFINE_MUTEX(chardev_mutex); ++ +/* + * Reads from device, displays in userspace, and deletes the read data + */ @@ -547,38 +567,42 @@ index 0000000..8696a99 + int remaining_length = return_size - *offset; + + deb_info("guest: read gpio chardev\n"); -+ deb_verbose("guest: read op: len = %ld, offset = %lld, *return_buffer = 0x%016llX\n", len, *offset, *(uint64_t *)return_buffer); -+ hexDump (DEVICE_NAME, "Chardev (guest read) dump buffer", return_buffer, len); -+ //deb_verbose("guest: read op: len = %ld, offset = %lld, *return_value = 0x%016llX\n", len, *offset, *return_value); ++ deb_verbose("guest: read op: remaining_length = %d, len = %ld, offset = %lld, return_value = 0x%016llX\n", remaining_length, len, *offset, return_value); ++ // hexDump (DEVICE_NAME, "Chardev (guest read) dump buffer", return_buffer, len); ++ //deb_verbose("guest: read op: len = %ld, offset = %lld, *return_value = 0x%016llX\n", len, *offset, return_value); + // hexDump (DEVICE_NAME, "Chardev (guest read) dump buffer", (char *)return_value, return_size); + -+ if ( remaining_length < 0 ) { -+ deb_info("guest: unrecoverable length *error*, remaining_length = %d\n", remaining_length); ++ if ( *offset == 0 ) ++ mutex_lock(&chardev_mutex); // activate mutex on entry ++ ++ if ( remaining_length < 0 ) { ++ deb_error("guest: unrecoverable length *error*, remaining_length = %d\n", remaining_length); + return -EINVAL; + } + + if ( len > remaining_length ) { -+ deb_info("guest: recoverable length *error*, len = %ld, remaining_length = %d, return_size = %d\n", len, remaining_length, return_size); -+ len = remaining_length - *offset; ++ deb_error("guest: recoverable length *error*, len = %ld, remaining_length = %d, return_size = %d\n", len, remaining_length, return_size); ++ len = remaining_length; + } + -+ if (copy_to_user(buf + *offset, (char *)return_buffer + *offset, len)) { -+ deb_info("guest: failed to copy to user\n"); ++ if (copy_to_user(buf, (char *)return_buffer + *offset, len)) { ++ deb_error("guest: failed to copy to user\n"); + return -EFAULT; + } -+ + *offset += len; ++ remaining_length -= len; + + // Check if all data was copied + if (remaining_length > len) { -+ deb_info("guest: not all bytes were copied\n"); -+ // If not, set the error status and return the number of bytes actually copied -+ // return -EINVAL; ++ deb_debug("guest: not all bytes were copied\n"); + } -+ else if (remaining_length == len) { ++ else if (remaining_length == 0) { ++ // read is complete + deb_verbose("reset return_size\n"); + return_size = 0; ++ *offset = 0; + memset(return_buffer, 0, RET_SIZE); ++ mutex_unlock(&chardev_mutex); // allow next message + } + + // Indicate success by returning the number of bytes read @@ -592,14 +616,16 @@ index 0000000..8696a99 +static ssize_t write(struct file *filep, const char *buffer, size_t len, loff_t *offset) +{ + struct tegra_gpio_pt *kbuf = NULL; -+ tegra_gpio_pt_extended *kbuf_ext = NULL; -+ struct tegra_getbase_pt *kbuf_getbase = NULL; ++ struct tegra_gpio_pt_ext *kbuf_ext = NULL; + char *buffer_pos = (char *)buffer; -+ // unsigned char *mask; -+ void __iomem *ret_ptr = NULL; ++ void *ret_ptr = NULL; ++ uint64_t ret_64; + int ret_int; // 32 bits + int ret; + ++ _Static_assert( sizeof(ret_ptr) == sizeof(return_value), ++ "ret_ptr size does not match return_value" ); ++ + /* + static struct file *file; + static struct inode *inode = NULL; @@ -612,12 +638,11 @@ index 0000000..8696a99 + deb_info("## writeing %zu bytes to chardev ##", len); + + if( len != sizeof(struct tegra_gpio_pt) && -+ len != sizeof(struct tegra_getbase_pt) && -+ len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) { -+ pr_err("Illegal chardev data length. Expected %ld, %ld, %ld or %ld, but got %ld\n", ++ len != sizeof(struct tegra_gpio_pt_ext) && ++ len != sizeof(struct tegra_readl_writel) ) { ++ deb_error("Illegal chardev data length. Expected %ld, %ld or %ld, but got %ld\n", + sizeof(struct tegra_gpio_pt), -+ sizeof(struct tegra_getbase_pt), -+ sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), ++ sizeof(struct tegra_gpio_pt_ext), + sizeof(struct tegra_readl_writel), + len); + hexDump (DEVICE_NAME, "Chardev (guest) input error", buffer, len); @@ -625,7 +650,7 @@ index 0000000..8696a99 + } + + if(!offset) { -+ pr_err("offset pointer is NULL, ignoring offset\n"); ++ deb_error("offset pointer is NULL, ignoring offset\n"); + } + else { + buffer_pos += (*offset); @@ -633,7 +658,7 @@ index 0000000..8696a99 + + kbuf = kmalloc(len, GFP_KERNEL); + if ( !kbuf ) { -+ pr_err("kbuf memory allocation failed\n"); ++ deb_error("kbuf memory allocation failed\n"); + len = -ENOMEM; + goto exit; + } @@ -642,20 +667,20 @@ index 0000000..8696a99 + + // Copy header + if (copy_from_user(kbuf, buffer_pos, len)) { -+ pr_err("copy_from_user failed\n"); ++ deb_error("copy_from_user failed\n"); + len = -ENOMEM; + goto exit; + } + buffer_pos += RETURN_OFF; + -+ // we are not checking if tegra_gpio_pt_extended is used, we only check for memory allocation -+ if( len == (sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) ) { -+ kbuf_ext = (tegra_gpio_pt_extended *)(kbuf + 1); ++ // we are not checking if tegra_gpio_pt_ext is used, we only check for memory allocation ++ if( len == sizeof(struct tegra_gpio_pt_ext) ) { ++ kbuf_ext = (struct tegra_gpio_pt_ext *)(kbuf); + deb_verbose("kbuf_ext is set up at kbuf_ext=%p", kbuf_ext); + } + + // print copied user parameters -+ hexDump (DEVICE_NAME, "Chardev input", kbuf, len); ++ hexDump (DEVICE_NAME, "Chardev input " DEVICE_NAME, kbuf, len); + + // make chardev type call to gpio + deb_verbose("Passthrough from guest with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); @@ -665,13 +690,13 @@ index 0000000..8696a99 + #ifdef GPIO_DEBUG_VERBOSE + chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); + if(chip != chip_alt) { -+ deb_debug("conflicting chip pointers -- primary %p, alternative %p", chip, chip_alt); ++ deb_debug("conflicting chip pointers -- primary 0x%p, alternative 0x%p", chip, chip_alt); + chip = chip_alt; // we assume find_chip_by_name is more reliable + } + #endif + */ + if(!chip) { -+ pr_err("chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); ++ deb_error("chip pointer's pvalue is unexpectedly NULL for chip\n"); + len = -ENODEV; + goto exit; + } @@ -680,13 +705,13 @@ index 0000000..8696a99 + case GPIO_REQ: + deb_verbose("GPIO_REQ, using GPIO chip %s, for device %d\n", chip->label, kbuf->chipnum); + ret_int = chip->request(chip, kbuf->offset); -+ goto end; ++ goto retval; + break; + case GPIO_FREE: + deb_verbose("GPIO_FREE\n"); + chip->free(chip, kbuf->offset); + // chip_alt = NULL; -+ goto end; ++ goto noretval; + break; + case GPIO_GET_DIR: + deb_verbose("GPIO_GET_DIR\n"); @@ -711,12 +736,12 @@ index 0000000..8696a99 + case GPIO_SET: + deb_verbose("GPIO_SET, set %d at offset 0x%x in gpiochip %s\n", kbuf->level, kbuf->offset, chip->label); + chip->set(chip, kbuf->offset, kbuf->level); -+ goto end; ++ goto noretval; + break; + case GPIO_CONFIG: + deb_verbose("GPIO_CONFIG\n"); -+ chip->set_config(chip, kbuf->offset, kbuf_ext->config); // arg mapped to unsigned long config -+ goto end; ++ ret_int = chip->set_config(chip, kbuf_ext->base.offset, kbuf_ext->ext.config); // arg mapped to unsigned long config ++ goto retval; + break; + case GPIO_TIMESTAMP_CTRL: + deb_verbose("GPIO_TIMESTAMP_CTRL\n"); @@ -725,22 +750,23 @@ index 0000000..8696a99 + break; + case GPIO_TIMESTAMP_READ: + deb_verbose("GPIO_TIMESTAMP_READ\n"); -+ ret = chip->timestamp_read(chip, kbuf->offset, (uint64_t *)buffer_pos); // timestamp is u64, return value as pointer ++ ret = chip->timestamp_read(chip, kbuf->offset, &ret_64); // timestamp is u64, return value as pointer ++ deb_verbose("timestamp_read: 0x%016llX\n", ret_64); + if(ret) { -+ pr_err("GPIO_TIMESTAMP_READ error\n"); -+ goto end; ++ deb_error("GPIO_TIMESTAMP_READ error\n"); ++ ret_64 = ret; + } -+ // timestamp_read returns value directly to buffer_pos -+ goto end; ++ deb_verbose("timestamp_read: 0x%016llX\n", ret_64); ++ goto ret64; + break; + case GPIO_SUSPEND_CONF: + deb_verbose("GPIO_SUSPEND_CONF\n"); + if(!kbuf_ext) { -+ pr_err("Parameter error in GPIO_SUSPEND_CONF\n"); ++ deb_error("Parameter error in GPIO_SUSPEND_CONF\n"); + len = -EINVAL; + goto exit; + } -+ ret_int = chip->suspend_configure(chip, kbuf->offset, kbuf_ext->dflags); ++ ret_int = chip->suspend_configure(chip, kbuf_ext->base.offset, kbuf_ext->ext.dflags); + goto retval; + break; + case GPIO_ADD_PINRANGES: @@ -750,12 +776,12 @@ index 0000000..8696a99 + break; + case TEGRA_186_GETBASE: + deb_verbose("TEGRA_186_GETBASE\n"); -+ kbuf_getbase = (void *)kbuf; -+ ret_ptr = tegra186_gpio_get_base_redirect(kbuf_getbase->chipnum, kbuf_getbase->pin); -+ goto retptr; // 64 bit? ++ ret_ptr = tegra186_gpio_get_base_redirect(kbuf_ext->base.chipnum, kbuf_ext->ext.pin); ++ deb_verbose("tegra186_gpio_get_base_execute, chip: %d, pointer: 0x%p / 0x%016llX\n", kbuf_ext->base.chipnum, ret_ptr, (uint64_t)ret_ptr); ++ goto retptr; // 64 bit + break; + default: -+ pr_err("GPIO, Unknown passthough signal\n"); ++ deb_error("GPIO, Unknown passthough signal\n"); + len = -EPERM; + goto exit; + break; @@ -796,7 +822,7 @@ index 0000000..8696a99 + case GPIO_CHARDEV_OPEN: // .open = gpio_chrdev_open + file = filp_open(tegra_chiplabel[kbuf->chipnum], O_RDWR, 0); + if (IS_ERR(file)) { -+ pr_err("GPIO, failed to open chardev for chip %s: %ld", tegra_chiplabel[kbuf->chipnum], PTR_ERR(file)); ++ deb_error("GPIO, failed to open chardev for chip %s: %ld", tegra_chiplabel[kbuf->chipnum], PTR_ERR(file)); + len = -ENOENT; + goto exit; + } @@ -809,17 +835,17 @@ index 0000000..8696a99 + case GPIO_CHARDEV_IOCTL: // .unlocked_ioctl = gpio_ioctl + // user space triggers gpio_ioctl -- it is .unlocked_ioctl on the chardev + if( !file ) { -+ pr_err("GPIO, chardev file was expected to be open\n"); ++ deb_error("GPIO, chardev file was expected to be open\n"); + len = -ENOENT; + goto exit; + } + // defined as: static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+ ret_l = file->f_op->unlocked_ioctl(file, kbuf->cmd, kbuf_ext->arg); // arg is pointer data which should have been copied from userspace ++ ret_l = file->f_op->unlocked_ioctl(file, kbuf->cmd, kbuf_ext->ext.arg); // arg is pointer data which should have been copied from userspace + goto retlong; + break; + case GPIO_CHARDEV_RELEASE: // .release = gpio_chrdev_release + if( !file ) { -+ pr_err("GPIO, chardev file was expected to be open\n"); ++ deb_error("GPIO, chardev file was expected to be open\n"); + len = -ENOENT; + goto exit; + } @@ -829,37 +855,37 @@ index 0000000..8696a99 + break; + case GPIO_CHARDEV_POLL: // .poll = lineinfo_watch_poll + if( !file ) { -+ pr_err("GPIO, chardev file was expected to be open\n"); ++ deb_error("GPIO, chardev file was expected to be open\n"); + len = -ENOENT; + goto exit; + } + // defined as: static __poll_t lineinfo_watch_poll(struct file *file, struct poll_table_struct *pollt) -+ ret_int = file->f_op->poll(file, kbuf_ext->poll); // TODO arg is pointer data which should have been copied ++ ret_int = file->f_op->poll(file, kbuf_ext->ext.poll); // TODO arg is pointer data which should have been copied + goto retval; // __poll_t is of size unsigned int + break; + case GPIO_CHARDEV_READ: // .read = lineinfo_watch_read + if( !file ) { -+ pr_err("GPIO, chardev file was expected to be open\n"); ++ deb_error("GPIO, chardev file was expected to be open\n"); + len = -ENOENT; + goto exit; + } + // defined as: static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, size_t count, loff_t *off) -+ ret = file->f_op->read(file, buffer_pos, kbuf_ext->count, NULL); // ++ ret = file->f_op->read(file, buffer_pos, kbuf_ext->ext.count, NULL); // + if (ret) { -+ pr_err("Reading lineinfo returned zero\n"); ++ deb_error("Reading lineinfo returned zero\n"); + len = -EFAULT; + goto exit; + } + break; + case GPIO_CHARDEV_OWNER: // .owner = THIS_MODULE + if (copy_to_user(buffer_pos, file->f_op->owner->name, strlen(file->f_op->owner->name)+1)) { -+ pr_err("GPIO, copying user return value failed\n"); ++ deb_error("GPIO, copying user return value failed\n"); + len = -EFAULT; + goto exit; + } + break; + default: -+ pr_err("GPIO, Unknown passthough signal\n"); ++ deb_error("GPIO, Unknown passthough signal\n"); + len = -EPERM; + goto exit; + break; @@ -867,37 +893,69 @@ index 0000000..8696a99 + }; + */ + -+ goto end; ++ goto noretval; ++ ++ ret64: ++ mutex_lock(&chardev_mutex); // wait for read ++ return_size = sizeof(ret_64); ++ return_value = ret_64; ++ // memcpy(return_buffer, &ret_64, return_size); ++ deb_verbose("ret_64 (guest): 0x%p, 0x%016llX", ret_ptr, return_value); ++ goto ret_end; ++ // goto ret_extra_hack; + + retptr: ++ mutex_lock(&chardev_mutex); // wait for read + return_size = sizeof(ret_ptr); -+ memcpy(return_buffer, &ret_ptr, return_size); -+ deb_verbose("retval pointer (guest): 0x%p, 0x%016llX", ret_ptr, (uint64_t)ret_ptr); -+ goto end; ++ return_value = (uint64_t)ret_ptr; ++ // memcpy(return_buffer, &ret_ptr, return_size); ++ deb_verbose("retval pointer (guest): 0x%p, 0x%016llX", ret_ptr, return_value); ++ goto ret_end; + + retval: ++ mutex_lock(&chardev_mutex); // wait for read + return_size = sizeof(ret_int); + memcpy(return_buffer, &ret_int, return_size); + deb_verbose("retval int (guest): 0x%X", ret_int); ++ goto ret_end; ++ ++ noretval: ++ return_size = 0; ++ goto exit; ++ ++ /* ++ ret_extra_hack: ++ // uses memory space used only by long messages, can be used only with short messages (used for GPIO_TIMESTAMP_READ only) ++ // we assume ret_end is used normally ++ if ( copy_to_user( (char *)buffer_pos - sizeof(uint64_t), &ret, sizeof(ret)) ) { ++ deb_error("GPIO, copying user return value in \"hack\" failed: 0x%08X\n", ret); ++ len = -EFAULT; ++ } ++ // no need to update len because it is done in ret_end ++ // continue to ret_end ++ */ + -+ end: -+ *offset = RETURN_OFF; -+ len = *offset; -+ len += sizeof(return_buffer); -+ -+ if (MEM_SIZE >= return_size + RETURN_OFF) { -+ if ( (ret = copy_to_user( (char *)buffer_pos, return_buffer, sizeof(return_buffer))) ) { -+ pr_err("GPIO, copying user return value failed: 0x%08X\n", ret); -+ len = -EFAULT; -+ goto exit; ++ ret_end: ++ if ( return_size && return_size <= sizeof(return_value) ) { ++ if ( MEM_SIZE >= sizeof(return_value) + RETURN_OFF) { ++ // if this chardev is closed in write(), we lose the return value in buffer_pos. That's why 'return_buffer' is a static global var. ++ if ( (ret = copy_to_user( (char *)buffer_pos, return_buffer, sizeof(return_value))) ) { ++ deb_error("GPIO, copying user return value failed: 0x%08X\n", ret); ++ len = -EFAULT; ++ goto unlock; ++ } ++ // let Qemu detect we wrote a return value ++ len = RETURN_OFF + return_size; ++ deb_verbose("return value size %d copied to buffer (guest): 0x%p / 0x%016llX", return_size, (void *)return_value, return_value); ++ // hexDump(DEVICE_NAME, "Chardev (host write) dump buffer", return_buffer, MEM_SIZE); ++ } else { ++ len = -EINVAL; // Buffer too small ++ goto unlock; + } -+ len += sizeof(return_buffer); -+ } else { -+ len = -EINVAL; // Buffer too small -+ goto exit; + } -+ // deb_verbose("retval copied to buffer (guest): 0x%016llX", *(uint64_t *)return_buffer); -+ // hexDump(DEVICE_NAME, "Chardev (guest write) dump buffer", return_buffer, MEM_SIZE); ++ ++ unlock: ++ mutex_unlock(&chardev_mutex); // allow next write in case retval is received before chardev close and read() is never called + + exit: + kfree(kbuf); @@ -928,10 +986,10 @@ index 0000000..c2e0184 +obj-$(CONFIG_TEGRA_GPIO_HOST_PROXY) += gpio-host-proxy.o diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.c b/drivers/gpio-host-proxy/gpio-host-proxy.c new file mode 100644 -index 0000000..fd3956b +index 0000000..399befa --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.c -@@ -0,0 +1,757 @@ +@@ -0,0 +1,805 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO host Proxy Kernel Module @@ -997,7 +1055,8 @@ index 0000000..fd3956b + +#define RET_SIZE 8 // should be sizeof(uint64_t) +// static char return_buffer[MEM_SIZE]; // using the same size as the input buffer -+static char return_buffer[RET_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0}; ++static uint64_t return_value = 0; ++static char *return_buffer = (char *)&return_value; +static unsigned int return_size = 0; + +_Static_assert(sizeof(uint64_t) == RET_SIZE, "return size assertion for RET_SIZE failed"); @@ -1131,7 +1190,6 @@ index 0000000..fd3956b + */ +static int gpio_host_proxy_probe(struct platform_device *pdev) +{ -+ // int i; + deb_info("installing module.\n"); + + // ********************* @@ -1143,7 +1201,7 @@ index 0000000..fd3956b + // "allowed-clocks", gpio_ares.clock, 0, GPIO_HOST_MAX_CLOCKS_SIZE); + // + // if(gpio_ares.clocks_size <= 0){ -+ // pr_err("No allowed clocks defined\n"); ++ // deb_error("No allowed clocks defined\n"); + // return EINVAL; + // } + // @@ -1156,7 +1214,7 @@ index 0000000..fd3956b + // "allowed-resets", gpio_ares.reset, 0, GPIO_HOST_MAX_RESETS_SIZE); + // + // if(gpio_ares.resets_size <= 0){ -+ // pr_err("No allowed resets defined\n"); ++ // deb_error("No allowed resets defined\n"); + // return EINVAL; + // } + // @@ -1171,20 +1229,20 @@ index 0000000..fd3956b + major_number = register_chrdev(0, DEVICE_NAME, &fops); + if (major_number < 0) + { -+ pr_err("could not register number.\n"); ++ deb_error("could not register number.\n"); + return major_number; + } -+ deb_info("registered correctly with major number %d", major_number); ++ deb_debug("registered correctly with major number %d", major_number); + + // Register the device class + gpio_host_proxy_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(gpio_host_proxy_class)) + { // Check for error and clean up if there is + unregister_chrdev(major_number, DEVICE_NAME); -+ pr_err("Failed to register device class\n"); ++ deb_error("Failed to register device class\n"); + return PTR_ERR(gpio_host_proxy_class); // Correct way to return an error on a pointer + } -+ deb_info("device class registered correctly\n"); ++ deb_debug("device class registered correctly\n"); + + // Register the device driver + gpio_host_proxy_device = device_create(gpio_host_proxy_class, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME); @@ -1192,11 +1250,11 @@ index 0000000..fd3956b + { // Clean up if there is an error + class_destroy(gpio_host_proxy_class); + unregister_chrdev(major_number, DEVICE_NAME); -+ pr_err("Failed to create the device\n"); ++ deb_error("Failed to create the device\n"); + return PTR_ERR(gpio_host_proxy_device); + } + -+ deb_info("device class created correctly\n"); // Made it! device was initialized ++ deb_debug("device class created correctly\n"); // Made it! device was initialized + + return 0; +} @@ -1211,7 +1269,7 @@ index 0000000..fd3956b + class_unregister(gpio_host_proxy_class); // unregister the device class + class_destroy(gpio_host_proxy_class); // remove the device class + unregister_chrdev(major_number, DEVICE_NAME); // unregister the major number -+ deb_info("Goodbye from the LKM!\n"); ++ deb_info("Goodbye from the GPIO passthrough\n"); + unregister_chrdev(major_number, DEVICE_NAME); + return 0; +} @@ -1234,6 +1292,8 @@ index 0000000..fd3956b + return 0; +} + ++static DEFINE_MUTEX(chardev_mutex); ++ +/* + * Reads from device, displays in userspace, and deletes the read data + */ @@ -1241,38 +1301,42 @@ index 0000000..fd3956b + int remaining_length = return_size - *offset; + + deb_info("host: read gpio chardev\n"); -+ deb_verbose("host: read op, len = %ld, offset = %lld, *return_buffer = 0x%016llX\n", len, *offset, *(uint64_t *)return_buffer); -+ hexDump (DEVICE_NAME, "Chardev (host read) dump buffer", return_buffer, len); -+ //deb_verbose("host: read op: len = %ld, offset = %lld, *return_value = 0x%016llX\n", len, *offset, *return_value); ++ deb_verbose("host: read op: remaining_length = %d, len = %ld, offset = %lld, return_value = 0x%016llX\n", remaining_length, len, *offset, return_value); ++ // hexDump (DEVICE_NAME, "Chardev (host read) dump buffer", return_buffer, len); ++ //deb_verbose("host: read op: len = %ld, offset = %lld, *return_value = 0x%016llX\n", len, *offset, return_value); + // hexDump (DEVICE_NAME, "Chardev (host read) dump buffer", (char *)return_value, return_size); + ++ if ( *offset == 0 ) ++ mutex_lock(&chardev_mutex); // activate mutex on entry ++ + if ( remaining_length < 0 ) { -+ deb_info("host: unrecoverable length *error*, remaining_length = %d\n", remaining_length); ++ deb_error("host: unrecoverable length *error*, remaining_length = %d\n", remaining_length); + return -EINVAL; + } + + if ( len > remaining_length ) { -+ deb_info("host: recoverable length *error*, len = %ld, remaining_length = %d, return_size = %d\n", len, remaining_length, return_size); -+ len = remaining_length - *offset; ++ deb_error("host: recoverable length *error*, len = %ld, remaining_length = %d, return_size = %d\n", len, remaining_length, return_size); ++ len = remaining_length; + } + -+ if (copy_to_user(buf + *offset, (char *)return_buffer + *offset, len)) { -+ deb_info("host: failed to copy to user\n"); ++ if (copy_to_user(buf, (char *)return_buffer + *offset, len)) { ++ deb_error("host: failed to copy to user\n"); + return -EFAULT; + } -+ + *offset += len; ++ remaining_length -= len; + + // Check if all data was copied + if (remaining_length > len) { -+ deb_info("host: not all bytes were copied\n"); -+ // If not, set the error status and return the number of bytes actually copied -+ // return -EINVAL; ++ deb_debug("host: not all bytes were copied\n"); + } -+ else if (remaining_length == len) { ++ else if (remaining_length == 0) { ++ // read is complete + deb_verbose("reset return_size\n"); + return_size = 0; ++ *offset = 0; + memset(return_buffer, 0, RET_SIZE); ++ mutex_unlock(&chardev_mutex); // allow next message + } + + // Indicate success by returning the number of bytes read @@ -1289,14 +1353,16 @@ index 0000000..fd3956b +{ + struct tegra_gpio_pt *kbuf = NULL; + struct tegra_readl_writel *kbuf_rw = NULL; -+ tegra_gpio_pt_extended *kbuf_ext = NULL; -+ struct tegra_getbase_pt *kbuf_getbase = NULL; ++ struct tegra_gpio_pt_ext *kbuf_ext = NULL; + char *buffer_pos = (char *)buffer; -+ // unsigned char *mask; -+ void __iomem *ret_ptr = NULL; ++ void *ret_ptr = NULL; ++ uint64_t ret_64; + int ret_int; // 32 bits + int ret; + ++ _Static_assert( sizeof(ret_ptr) == sizeof(return_value), ++ "ret_ptr size does not match return_value" ); ++ + /* + static struct file *file; + static struct inode *inode = NULL; @@ -1309,13 +1375,11 @@ index 0000000..fd3956b + deb_info("## writeing %zu bytes to chardev ##", len); + + if( len != sizeof(struct tegra_gpio_pt) && -+ len != sizeof(struct tegra_getbase_pt) && -+ len != sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) && -+ len != sizeof(struct tegra_readl_writel) ) { -+ pr_err("Illegal chardev data length. Expected %ld, %ld, %ld or %ld, but got %ld\n", ++ len != sizeof(struct tegra_gpio_pt_ext) && ++ len != sizeof(struct tegra_readl_writel) ) { ++ deb_error("Illegal chardev data length. Expected %ld, %ld or %ld, but got %ld\n", + sizeof(struct tegra_gpio_pt), -+ sizeof(struct tegra_getbase_pt), -+ sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended), ++ sizeof(struct tegra_gpio_pt_ext), + sizeof(struct tegra_readl_writel), + len); + hexDump (DEVICE_NAME, "Chardev (host) input error", buffer, len); @@ -1323,7 +1387,7 @@ index 0000000..fd3956b + } + + if(!offset) { -+ pr_err("offset pointer is NULL, ignoring offset\n"); ++ deb_error("offset pointer is NULL, ignoring offset\n"); + } + else { + buffer_pos += (*offset); @@ -1331,7 +1395,7 @@ index 0000000..fd3956b + + kbuf = kmalloc(len, GFP_KERNEL); + if ( !kbuf ) { -+ pr_err("kbuf memory allocation failed\n"); ++ deb_error("kbuf memory allocation failed\n"); + len = -ENOMEM; + goto exit; + } @@ -1340,20 +1404,20 @@ index 0000000..fd3956b + + // Copy header + if (copy_from_user(kbuf, buffer_pos, len)) { -+ pr_err("copy_from_user failed\n"); ++ deb_error("copy_from_user failed\n"); + len = -ENOMEM; + goto exit; + } + buffer_pos += RETURN_OFF; + -+ // we are not checking if tegra_gpio_pt_extended is used, we only check for memory allocation -+ if( len == (sizeof(struct tegra_gpio_pt) + sizeof(tegra_gpio_pt_extended) ) ) { -+ kbuf_ext = (tegra_gpio_pt_extended *)(kbuf + 1); ++ // we are not checking if tegra_gpio_pt_ext is used, we only check for memory allocation ++ if( len == sizeof(struct tegra_gpio_pt_ext) ) { ++ kbuf_ext = (struct tegra_gpio_pt_ext *)(kbuf); + deb_verbose("kbuf_ext is set up at kbuf_ext=%p", kbuf_ext); + } + + // print copied user parameters -+ hexDump (DEVICE_NAME, "Chardev input", kbuf, len); ++ hexDump (DEVICE_NAME, "Chardev input " DEVICE_NAME, kbuf, len); + + // make chardev type call to gpio + deb_verbose("Passthrough in host with signal: %c, Chip %d, Offset %d, Level %d", kbuf->signal, kbuf->chipnum, kbuf->offset, kbuf->level); @@ -1361,8 +1425,11 @@ index 0000000..fd3956b + switch (kbuf->signal) { + case GPIO_READL: + kbuf_rw = (struct tegra_readl_writel *)kbuf; -+ if( kbuf_rw->address == 0 || !access_ok(kbuf_rw->address, sizeof(u32)) ) { -+ deb_info("*error* cannot access address 0x%p (probably an address in guest space)", kbuf_rw->address); ++ deb_verbose("readl accessing address 0x%p / 0x%016llX", kbuf_rw->address, (uint64_t)kbuf_rw->address); ++ // if( kbuf_rw->address == 0 || !access_ok(kbuf_rw->address, sizeof(u32)) ) { ++ // deb_error("*error* cannot access address 0x%p / 0x%016llX (probably an address in guest space)", kbuf_rw->address, (uint64_t)kbuf_rw->address); ++ if( kbuf_rw->address == 0 ) { ++ deb_error("address was null in readl passthrough\n"); + ret_int = 0xDEADBEEF; + goto readl_addr_error; + } @@ -1382,8 +1449,11 @@ index 0000000..fd3956b + break; + case GPIO_WRITEL: + kbuf_rw = (struct tegra_readl_writel *)kbuf; -+ if( kbuf_rw->address == 0 || !access_ok(kbuf_rw->address, sizeof(u32)) ) { -+ deb_info("*error* cannot access address 0x%p (probably an address in guest space)", kbuf_rw->address); ++ deb_verbose("readl accessing address 0x%p / 0x%016llX", kbuf_rw->address, (uint64_t)kbuf_rw->address); ++ // if( kbuf_rw->address == 0 || !access_ok(kbuf_rw->address, sizeof(u32)) ) { ++ // deb_error("*error* cannot access address 0x%p / 0x%016llX (probably an address in guest space)", kbuf_rw->address, (uint64_t)kbuf_rw->address); ++ if( kbuf_rw->address == 0 ) { ++ deb_error("address was null in writel passthrough\n"); + goto writel_addr_error; + } + switch (kbuf_rw->rwltype) { @@ -1396,25 +1466,25 @@ index 0000000..fd3956b + case RWL_RELAXED: + writel_relaxed(kbuf_rw->value, kbuf_rw->address); + break; -+ writel_addr_error: -+ goto end; + } ++ writel_addr_error: ++ goto noretval; + break; + } -+ // if switch above is triggered we will either goto retval or goto end ++ // if switch above is triggered we will either goto retval or goto noretval + + chip = find_chip_by_id(kbuf->chipnum); + /* + #ifdef GPIO_DEBUG_VERBOSE + chip_alt = find_chip_by_name(tegra_chiplabel[kbuf->chipnum]); + if(chip != chip_alt) { -+ deb_debug("conflicting chip pointers -- primary %p, alternative %p", chip, chip_alt); ++ deb_debug("conflicting chip pointers -- primary 0x%p, alternative 0x%p", chip, chip_alt); + chip = chip_alt; // we assume find_chip_by_name is more reliable + } + #endif + */ + if(!chip) { -+ pr_err("chip pointer's pvalue is unexpectedly NULL for chip %s\n", tegra_chiplabel[kbuf->chipnum]); ++ deb_error("chip pointer's pvalue is unexpectedly NULL for chip\n"); + len = -ENODEV; + goto exit; + } @@ -1423,13 +1493,13 @@ index 0000000..fd3956b + case GPIO_REQ: + deb_verbose("GPIO_REQ, using GPIO chip %s, for device %d\n", chip->label, kbuf->chipnum); + ret_int = chip->request(chip, kbuf->offset); -+ goto end; ++ goto retval; + break; + case GPIO_FREE: + deb_verbose("GPIO_FREE\n"); + chip->free(chip, kbuf->offset); + // chip_alt = NULL; -+ goto end; ++ goto noretval; + break; + case GPIO_GET_DIR: + deb_verbose("GPIO_GET_DIR\n"); @@ -1454,12 +1524,12 @@ index 0000000..fd3956b + case GPIO_SET: + deb_verbose("GPIO_SET, set %d at offset 0x%x in gpiochip %s\n", kbuf->level, kbuf->offset, chip->label); + chip->set(chip, kbuf->offset, kbuf->level); -+ goto end; ++ goto noretval; + break; + case GPIO_CONFIG: + deb_verbose("GPIO_CONFIG\n"); -+ chip->set_config(chip, kbuf->offset, kbuf_ext->config); // arg mapped to unsigned long config -+ goto end; ++ ret_int = chip->set_config(chip, kbuf_ext->base.offset, kbuf_ext->ext.config); // arg mapped to unsigned long config ++ goto retval; + break; + case GPIO_TIMESTAMP_CTRL: + deb_verbose("GPIO_TIMESTAMP_CTRL\n"); @@ -1468,22 +1538,23 @@ index 0000000..fd3956b + break; + case GPIO_TIMESTAMP_READ: + deb_verbose("GPIO_TIMESTAMP_READ\n"); -+ ret = chip->timestamp_read(chip, kbuf->offset, (uint64_t *)buffer_pos); // timestamp is u64, return value as pointer ++ ret = chip->timestamp_read(chip, kbuf->offset, &ret_64); // timestamp is u64, return value as pointer ++ deb_verbose("timestamp_read: 0x%016llX\n", ret_64); + if(ret) { -+ pr_err("GPIO_TIMESTAMP_READ error\n"); -+ goto end; ++ deb_error("GPIO_TIMESTAMP_READ error\n"); ++ ret_64 = ret; + } -+ // timestamp_read returns value directly to buffer_pos -+ goto end; ++ deb_verbose("timestamp_read: 0x%016llX\n", ret_64); ++ goto ret64; + break; + case GPIO_SUSPEND_CONF: + deb_verbose("GPIO_SUSPEND_CONF\n"); + if(!kbuf_ext) { -+ pr_err("Parameter error in GPIO_SUSPEND_CONF\n"); ++ deb_error("Parameter error in GPIO_SUSPEND_CONF\n"); + len = -EINVAL; + goto exit; + } -+ ret_int = chip->suspend_configure(chip, kbuf->offset, kbuf_ext->dflags); ++ ret_int = chip->suspend_configure(chip, kbuf_ext->base.offset, kbuf_ext->ext.dflags); + goto retval; + break; + case GPIO_ADD_PINRANGES: @@ -1493,12 +1564,12 @@ index 0000000..fd3956b + break; + case TEGRA_186_GETBASE: + deb_verbose("TEGRA_186_GETBASE\n"); -+ kbuf_getbase = (void *)kbuf; -+ ret_ptr = tegra186_gpio_get_base_execute(kbuf_getbase->chipnum, kbuf_getbase->pin); -+ goto retptr; // 64 bit? ++ ret_ptr = tegra186_gpio_get_base_execute(kbuf_ext->base.chipnum, kbuf_ext->ext.pin); ++ deb_verbose("tegra186_gpio_get_base_execute, chip: %d, pointer: 0x%p / 0x%016llX\n", kbuf_ext->base.chipnum, ret_ptr, (uint64_t)ret_ptr); ++ goto retptr; // 64 bit + break; + default: -+ pr_err("GPIO, Unknown passthough signal\n"); ++ deb_error("GPIO, Unknown passthough signal\n"); + len = -EPERM; + goto exit; + break; @@ -1539,7 +1610,7 @@ index 0000000..fd3956b + case GPIO_CHARDEV_OPEN: // .open = gpio_chrdev_open + file = filp_open(tegra_chiplabel[kbuf->chipnum], O_RDWR, 0); + if (IS_ERR(file)) { -+ pr_err("GPIO, failed to open chardev for chip %s: %ld", tegra_chiplabel[kbuf->chipnum], PTR_ERR(file)); ++ deb_error("GPIO, failed to open chardev for chip %s: %ld", tegra_chiplabel[kbuf->chipnum], PTR_ERR(file)); + len = -ENOENT; + goto exit; + } @@ -1552,17 +1623,17 @@ index 0000000..fd3956b + case GPIO_CHARDEV_IOCTL: // .unlocked_ioctl = gpio_ioctl + // user space triggers gpio_ioctl -- it is .unlocked_ioctl on the chardev + if( !file ) { -+ pr_err("GPIO, chardev file was expected to be open\n"); ++ deb_error("GPIO, chardev file was expected to be open\n"); + len = -ENOENT; + goto exit; + } + // defined as: static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+ ret_l = file->f_op->unlocked_ioctl(file, kbuf->cmd, kbuf_ext->arg); // arg is pointer data which should have been copied from userspace ++ ret_l = file->f_op->unlocked_ioctl(file, kbuf->cmd, kbuf_ext->ext.arg); // arg is pointer data which should have been copied from userspace + goto retlong; + break; + case GPIO_CHARDEV_RELEASE: // .release = gpio_chrdev_release + if( !file ) { -+ pr_err("GPIO, chardev file was expected to be open\n"); ++ deb_error("GPIO, chardev file was expected to be open\n"); + len = -ENOENT; + goto exit; + } @@ -1572,37 +1643,37 @@ index 0000000..fd3956b + break; + case GPIO_CHARDEV_POLL: // .poll = lineinfo_watch_poll + if( !file ) { -+ pr_err("GPIO, chardev file was expected to be open\n"); ++ deb_error("GPIO, chardev file was expected to be open\n"); + len = -ENOENT; + goto exit; + } + // defined as: static __poll_t lineinfo_watch_poll(struct file *file, struct poll_table_struct *pollt) -+ ret_int = file->f_op->poll(file, kbuf_ext->poll); // TODO arg is pointer data which should have been copied ++ ret_int = file->f_op->poll(file, kbuf_ext->ext.poll); // TODO arg is pointer data which should have been copied + goto retval; // __poll_t is of size unsigned int + break; + case GPIO_CHARDEV_READ: // .read = lineinfo_watch_read + if( !file ) { -+ pr_err("GPIO, chardev file was expected to be open\n"); ++ deb_error("GPIO, chardev file was expected to be open\n"); + len = -ENOENT; + goto exit; + } + // defined as: static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, size_t count, loff_t *off) -+ ret = file->f_op->read(file, buffer_pos, kbuf_ext->count, NULL); // ++ ret = file->f_op->read(file, buffer_pos, kbuf_ext->ext.count, NULL); // + if (ret) { -+ pr_err("Reading lineinfo returned zero\n"); ++ deb_error("Reading lineinfo returned zero\n"); + len = -EFAULT; + goto exit; + } + break; + case GPIO_CHARDEV_OWNER: // .owner = THIS_MODULE + if (copy_to_user(buffer_pos, file->f_op->owner->name, strlen(file->f_op->owner->name)+1)) { -+ pr_err("GPIO, copying user return value failed\n"); ++ deb_error("GPIO, copying user return value failed\n"); + len = -EFAULT; + goto exit; + } + break; + default: -+ pr_err("GPIO, Unknown passthough signal\n"); ++ deb_error("GPIO, Unknown passthough signal\n"); + len = -EPERM; + goto exit; + break; @@ -1610,37 +1681,72 @@ index 0000000..fd3956b + }; + */ + -+ goto end; ++ goto noretval; ++ ++ ret64: ++ mutex_lock(&chardev_mutex); // wait for read ++ return_size = sizeof(ret_64); ++ return_value = ret_64; ++ // memcpy(return_buffer, &ret_64, return_size); ++ deb_verbose("ret_64 (host):0x%p, 0x%016llX", ret_ptr, return_value); ++ // TODO we want to return ret_64 and ret (calls for one more word in chardev total 4 words) ++ // for now: ret_64 is anyhow invalid if ret<0, interpreting negative value in ret_64 as error is unlikely (but not impossible) to fail ++ // if(ret < 0) ret_64 = ret; ++ goto ret_end; ++ // goto ret_extra_hack; + + retptr: ++ mutex_lock(&chardev_mutex); // wait for read + return_size = sizeof(ret_ptr); -+ memcpy(return_buffer, &ret_ptr, return_size); -+ deb_verbose("retval pointer (host): 0x%p, 0x%016llX", ret_ptr, (uint64_t)ret_ptr); -+ goto end; ++ return_value = (uint64_t)ret_ptr; ++ // memcpy(return_buffer, &ret_ptr, return_size); ++ deb_verbose("retval pointer (host): 0x%p, 0x%016llX", ret_ptr, return_value); ++ goto ret_end; + + retval: ++ mutex_lock(&chardev_mutex); // wait for read + return_size = sizeof(ret_int); + memcpy(return_buffer, &ret_int, return_size); -+ deb_verbose("retval int (host): 0x%X", ret); -+ -+ end: -+ *offset = RETURN_OFF; -+ len = *offset; -+ len += sizeof(return_buffer); -+ -+ if (MEM_SIZE >= return_size + RETURN_OFF) { -+ if ( (ret = copy_to_user( (char *)buffer_pos, return_buffer, sizeof(return_buffer))) ) { -+ pr_err("GPIO, copying user return value failed: 0x%08X\n", ret); -+ len = -EFAULT; -+ goto exit; ++ deb_verbose("retval int (host): 0x%X", ret_int); ++ goto ret_end; ++ ++ noretval: ++ return_size = 0; ++ goto exit; ++ ++ /* ++ ret_extra_hack: ++ // uses memory space used only by long messages, can be used only with short messages (used for GPIO_TIMESTAMP_READ only) ++ // we assume ret_end is used normally ++ if ( copy_to_user( (char *)buffer_pos - sizeof(uint64_t), &ret, sizeof(ret)) ) { ++ deb_error("GPIO, copying user return value in \"hack\" failed: 0x%08X\n", ret); ++ len = -EFAULT; ++ } ++ // no need to update len because it is done in ret_end ++ // continue to ret_end ++ */ ++ ++ ret_end: ++ if ( return_size && return_size <= sizeof(return_value) ) { ++ if ( MEM_SIZE >= sizeof(return_value) + RETURN_OFF) { ++ // if this chardev is closed in write(), we lose the return value in buffer_pos. That's why 'return_buffer' is a static global var. ++ if ( (ret = copy_to_user( (char *)buffer_pos, return_buffer, sizeof(return_value))) ) { ++ deb_error("GPIO, copying user return value failed: 0x%08X\n", ret); ++ len = -EFAULT; ++ goto unlock; ++ } ++ // let Qemu detect we wrote a return value ++ len = RETURN_OFF + return_size; ++ deb_verbose("return value size %d copied to buffer (host): 0x%p / 0x%016llX", return_size, (void *)return_value, return_value); ++ // hexDump(DEVICE_NAME, "Chardev (host write) dump buffer", return_buffer, MEM_SIZE); ++ } else { ++ len = -EINVAL; // Buffer too small ++ goto unlock; + } -+ len += sizeof(return_buffer); -+ } else { -+ len = -EINVAL; // Buffer too small -+ goto exit; + } -+ // deb_verbose("retval copied to buffer (host): 0x%016llX", *(uint64_t *)return_buffer); -+ // hexDump(DEVICE_NAME, "Chardev (host write) dump buffer", return_buffer, MEM_SIZE); ++ ++ unlock: ++ mutex_unlock(&chardev_mutex); // allow next write in case retval is received before chardev close and read() is never called + + exit: + kfree(kbuf); @@ -1671,7 +1777,7 @@ index 0000000..fd3956b + + ret = platform_driver_register(&gpio_host_proxy_driver); + if (ret != 0) { -+ pr_err("GPIO, Error %d registering gpio host proxy driver", ret); ++ deb_error("GPIO, Error %d registering gpio host proxy driver", ret); + } else { + deb_info("GPIO gpio host proxy driver registered successfully\n"); + } @@ -1691,10 +1797,10 @@ index 0000000..fd3956b +module_exit(gpio_host_proxy_exit); diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.h b/drivers/gpio-host-proxy/gpio-host-proxy.h new file mode 100644 -index 0000000..3aad6c5 +index 0000000..295c2b6 --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.h -@@ -0,0 +1,131 @@ +@@ -0,0 +1,132 @@ +#ifndef __GPIO_HOST_PROXY__H__ +#define __GPIO_HOST_PROXY__H__ + @@ -1800,29 +1906,30 @@ index 0000000..3aad6c5 +_Static_assert( sizeof(struct tegra_gpio_pt) == 4, + "tegra_gpio_pt size is not 4 bytes." ); + -+// struct __attribute__((packed)) tegra_gpio_pt { -+struct tegra_getbase_pt { -+ unsigned char chipnum; // lowest bit is number of gpio chip (gpiochip0 or gpiochip1), top 7 bits are message length -+ unsigned char signal; // defines operation -+ unsigned char pad[2]; -+ unsigned int pin; // chosen pins to process -+}; -+ -+_Static_assert( sizeof(struct tegra_getbase_pt) == 8, -+ "tegra_getbase_pt size is not 4 bytes." ); -+ -+union extended { -+ // int level; // pin level to be set -+ unsigned long config; // pin configuration ++union pt_extension { ++ // int level; // pin level to be set ++ unsigned long config; // pin configuration ++ unsigned int pin; // chosen pins to process + int enable; -+ size_t count; // lineinfo read size ++ size_t count; // lineinfo read size + struct poll_table_struct *poll; + enum gpiod_flags dflags; -+ u64 arg; // gpio_ioctl argument (this is interpreted as a pointer) ++ u64 arg; // gpio_ioctl argument (this is interpreted as a pointer) +}; + -+typedef union extended tegra_gpio_pt_extended; ++_Static_assert( sizeof(union pt_extension) == 8, ++ "tegra_gpio_pt_extension size is not 8 bytes." ); ++ ++ ++// typedef union pt_extension tegra_gpio_pt_extension; ++ ++struct tegra_gpio_pt_ext { ++ struct tegra_gpio_pt base; ++ union pt_extension ext; ++}; ++ ++_Static_assert( sizeof(struct tegra_gpio_pt_ext) == 16, ++ "tegra_gpio_pt_extension size is not 16 bytes." ); ++ + -+_Static_assert( sizeof(tegra_gpio_pt_extended) == 8, -+ "tegra_gpio_pt_extended size is not 8 bytes." ); +#endif diff --git a/modules/microvm/virtualization/microvm/simple-chardev-test.sh b/modules/microvm/virtualization/microvm/simple-chardev-test.sh index 33f121493..fd16ed74c 100755 --- a/modules/microvm/virtualization/microvm/simple-chardev-test.sh +++ b/modules/microvm/virtualization/microvm/simple-chardev-test.sh @@ -1,72 +1,86 @@ - # For pin names look at gpio_40pin_header.png - # some examples - # GPIO09 PBB.00 - gpiochip 1, offset 8/0x08 - # GPIO08 PBB.01 - gpiochip 1, offset 9/0x09 - # GPIO17 PP.04 - gpiochip 0, offset 96/0x60 - # GPIO27 PN.01 - gpiochip 0, offset 85/0x55 - # GPIO35 PH.00 - gpiochip 0, offset 43/0x2B - - # chipnum='\x00' # tegra234-gpio - # chipnum='\x01' # tegra234-gpio-aon - lvl0='\x00' - lvl1='\x01' - n_a='\x00' - offset='\x55' # example: PN.01/GPIO27 has line offset hex '\x55' - pad='\x00\x00\x00\x00' - # chardev='/dev/gpio-host' - chardev='/dev/gpio-guest' - - function res { - signal='r' # reserve line - echo -n -e ${signal}${chipnum}${n_a}${offset}${pad} >> ${chardev} - - signal='o' # set pin as output - # this will also set a level - echo -n -e ${signal}${chipnum}${lvl1}${offset}${pad} >> ${chardev} - } - - function setlevel { - echo -n -e ${signal}${chipnum}${level}${offset}${pad} >> ${chardev} - sleep 0.0050 - } - - function free { - signal='f' # free line - echo -n -e ${signal}${chipnum}${n_a}${offset}${pad} >> ${chardev} - } - -while true -do - sleep 10 - echo -n '.' - - chipnum='\x01';offset='\x08'; res - chipnum='\x01';offset='\x09'; res - chipnum='\x00';offset='\x2B'; res - chipnum='\x00';offset='\x55'; res - - i=10; - signal='s' # set level - while [ $i -gt 0 ] - do let i=$i-1 - - level='\x01' - chipnum='\x01';offset='\x08'; setlevel - chipnum='\x00';offset='\x55'; setlevel - chipnum='\x01';offset='\x09'; setlevel - chipnum='\x00';offset='\x2B'; setlevel - - level='\x00' - chipnum='\x01';offset='\x08'; setlevel - chipnum='\x00';offset='\x55'; setlevel - chipnum='\x01';offset='\x09'; setlevel - chipnum='\x00';offset='\x2B'; setlevel +# For pin names look at gpio_40pin_header.pn8 +# some examples +# GPIO09 PBB.00 - gpiochip 1, offset 8/0x00 +# GPIO08 PBB.01 - gpiochip 1, offset 9/0x09 +# GPIO17 PP.04 - gpiochip 0, offset 96/0x60 +# GPIO27 PN.01 - gpiochip 0, offset 85/0x55 +# GPIO35 PH.00 - gpiochip 0, offset 43/0x2B - done +chipnum='\x00' # tegra234-gpio +# chipnum='\x01' # tegra234-gpio-aon +offset='\x55' # PN.01/GPIO27 # line offset in hex + +lvl0='\x00' +lvl1='\x01' +n_a='\x00' + +# wait_time='0.0005' +wait_time='0' +chardev=$(ls /dev/gpio-[gh]*st) +echo -e "using ${chardev}\n" + +function read_ret { + echo "retval:" + dd if=${chardev} bs=1 count=4 | hexdump -C +} + +function res { + signal='r' # reserve line + echo -n -e ${chipnum}${signal}${n_a}${offset} >> ${chardev} + + read_ret + + signal='o' # set pin as output + # this will also set a level + echo -n -e ${chipnum}${signal}${lvl1}${offset} >> ${chardev} + + read_ret +} + +function setlevel { + signal='s' + echo -n -e ${chipnum}${signal}${level}${offset} >> ${chardev} + sleep ${wait_time} +} - chipnum='\x01';offset='\x02'; free - chipnum='\x01';offset='\x08'; free - chipnum='\x00';offset='\x2B'; free - chipnum='\x00';offset='\x55'; free +function getlevel { + signal='g' # set pin as output + echo -n -e ${chipnum}${signal}${level}${offset} >> ${chardev} + sleep ${wait_time} + + read_ret +} + +function freeline { + signal='f' # free line + echo -n -e ${chipnum}${signal}${n_a}${offset} >> ${chardev} +} + +chipnum='\x01';offset='\x08'; res +chipnum='\x00';offset='\x55'; res +chipnum='\x01';offset='\x09'; res +chipnum='\x00';offset='\x2B'; res + +i=10; +signal='s' # set level +while [ $i -gt 0 ] + do let i=$i-1 + + level='\x01' + chipnum='\x01';offset='\x08'; setlevel + chipnum='\x00';offset='\x55'; setlevel + chipnum='\x01';offset='\x09'; setlevel + chipnum='\x00';offset='\x2B'; setlevel + + level='\x00' + chipnum='\x01';offset='\x08'; setlevel + chipnum='\x00';offset='\x55'; setlevel + chipnum='\x01';offset='\x09'; setlevel + chipnum='\x00';offset='\x2B'; setlevel + + done -done +chipnum='\x01';offset='\x08'; freeline +chipnum='\x00';offset='\x55'; freeline +chipnum='\x01';offset='\x09'; freeline +chipnum='\x00';offset='\x2B'; freeline diff --git a/modules/microvm/virtualization/microvm/simple-gpiod-test.sh b/modules/microvm/virtualization/microvm/simple-gpiod-test.sh new file mode 100644 index 000000000..1e7fe5699 --- /dev/null +++ b/modules/microvm/virtualization/microvm/simple-gpiod-test.sh @@ -0,0 +1,45 @@ +# this script does a smoketest of gpiod functions on output pins + +# GPIO09 PBB.00 - gpiochip 1, offset 8/0x00 +# GPIO08 PBB.01 - gpiochip 1, offset 9/0x09 +# GPIO17 PP.04 - gpiochip 0, offset 96/0x60 +# GPIO27 PN.01 - gpiochip 0, offset 85/0x55 +# GPIO35 PH.00 - gpiochip 0, offset 43/0x2B + +# > gpioset -h +#Options: +# -h, --help: display this message and exit +# -v, --version: display the version and exit +# -l, --active-low: set the line active state to low +# -m, --mode=[exit|wait|time|signal] (defaults to 'exit'): +# tell the program what to do after setting values +# -s, --sec=SEC: specify the number of seconds to wait (only valid for --mode=time) +# -u, --usec=USEC: specify the number of microseconds to wait (only valid for --mode=time) +# -b, --background: after setting values: detach from the controlling terminal +# +#Modes: +# exit: set values and exit immediately +# wait: set values and wait for user to press ENTER +# time: set values and sleep for a specified amount of time +# signal: set values and wait for SIGINT or SIGTERM +# makes a smoketest on gpio functions on output pins + + +function setgroup { +gpioset 1 8=$1 +gpioset 0 85=$1 +gpioset 1 9=$1 +gpioset 0 43=$1 +} + +for i in $(seq 0 10) +do +setgroup 1 +setgroup 0 +done + +## commands below works in host (depends on gpiod version) +# gpioset -c0 -t10 8=0 +# gpioset -c0 -t10 85=0 +# gpioset -c0 -t10 9=0 +# gpioset -c0 -t10 43=0 From 3b37bb60526a21ce4997268feea9f6c3e06ae855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Tue, 3 Sep 2024 08:26:25 +0000 Subject: [PATCH 25/26] Merged with new tiiuae ghaf - added source patches: - kernel adfe63ef006a696e7384b9cee340f05bf7f4f1b3 from git@github.com:KimGSandstrom/tegra_kernel-5.10.git - gpio-virt patch d90bd5625957fd164e4eb50803bd724fda89ba68 from git@github.com:KimGSandstrom/gpio-virt.git - qemu 9.0.2 GPIO passthrough patch b077b3cb519958d091cd753f7015ffafd3bfcaee from git@github.com:KimGSandstrom/qemu-passthrough - GPIO passthrough works with provided qemu - microvm@gpio-vm.service does not start up correctly --- LICENSES/BSD-2-Clause-Patent.txt | 19 + LICENSES/GPL-2.0-only.txt | 117 + LICENSES/GPL-2.0-or-later.txt | 117 + LICENSES/GPL-3.0-only.txt | 232 + LICENSES/LGPL-2.1-or-later.txt | 176 + LICENSES/LicenseRef-NvidiaProprietary.txt | 265 + LICENSES/WTFPL.txt | 13 + README.md | 9 +- REUSE.toml | 127 + assets/ghaf-logo.png | Bin 6066 -> 0 bytes assets/icons/png/app.png | Bin 857 -> 0 bytes assets/icons/png/browser.png | Bin 1116 -> 0 bytes assets/icons/png/pdf.png | Bin 1133 -> 0 bytes assets/icons/png/settings.png | Bin 1335 -> 0 bytes assets/icons/png/windows.png | Bin 145 -> 0 bytes assets/icons/svg/app.svg | 27 - assets/icons/svg/browser.svg | 11 - assets/icons/svg/pdf.svg | 27 - assets/icons/svg/settings.svg | 9 - assets/icons/svg/windows.svg | 6 - assets/wallpaper.png | Bin 16129 -> 0 bytes default.nix | 14 +- docs/book.toml | 3 + docs/default.nix | 41 +- docs/plugins/mdbook-footnote.nix | 21 - docs/src/SUMMARY.md | 9 +- docs/src/appendices/glossary.md | 12 + docs/src/architecture/adr.md | 5 +- docs/src/architecture/adr/idsvm.md | 37 + docs/src/architecture/adr/minimal-host.md | 4 +- docs/src/architecture/adr/netvm.md | 35 +- .../adr/platform-bus-passthrough-support.md | 1 + docs/src/architecture/adr/template.md | 13 +- docs/src/architecture/architecture.md | 3 + docs/src/architecture/hardening.md | 10 +- docs/src/architecture/variants.md | 8 +- docs/src/features/features.md | 30 +- docs/src/img/idsvm.drawio.png | Bin 0 -> 35276 bytes docs/src/ref_impl/build_and_run.md | 43 +- docs/src/ref_impl/creating_appvm.md | 91 +- docs/src/ref_impl/cross_compilation.md | 1 + docs/src/ref_impl/development.md | 9 +- docs/src/ref_impl/example_project.md | 9 +- docs/src/ref_impl/hw-config.md | 26 + docs/src/ref_impl/idsvm-development.md | 36 + docs/src/ref_impl/installer.md | 28 +- docs/src/ref_impl/labwc.md | 5 +- docs/src/ref_impl/profiles-config.md | 48 + .../src/ref_impl/reference_implementations.md | 7 + docs/src/ref_impl/remote_build_setup.md | 4 +- docs/src/ref_impl/systemd-service-config.md | 614 ++ docs/src/release_notes/ghaf-23.05.md | 1 + docs/src/release_notes/ghaf-23.06.md | 2 +- docs/src/release_notes/ghaf-23.09.md | 6 +- docs/src/release_notes/ghaf-23.12.md | 4 +- docs/src/release_notes/ghaf-24.03.md | 12 +- docs/src/release_notes/ghaf-24.06.md | 93 + docs/src/release_notes/release_notes.md | 1 + docs/src/scenarios/run_cuttlefish.md | 1 + docs/src/scenarios/run_win_vm.md | 9 +- docs/src/scs/ci-cd-system.md | 1 + docs/src/scs/pki.md | 3 +- docs/src/technologies/hypervisor_options.md | 9 +- docs/src/technologies/nvidia_agx_pt_pcie.md | 9 +- docs/src/technologies/nvidia_agx_pt_uart.md | 5 +- .../nvidia_virtualization_bpmp.md | 89 +- docs/src/technologies/technologies.md | 2 + docs/src/technologies/x86_pcie_crosvm.md | 1 + docs/src/troubleshooting/README.md | 8 + .../troubleshooting/systemd/early-shell.md | 20 + docs/src/troubleshooting/systemd/index.md | 14 + docs/src/troubleshooting/systemd/strace.md | 26 + .../src/troubleshooting/systemd/system-log.md | 74 + docs/src/troubleshooting/systemd/systemctl.md | 79 + .../systemd/systemd-analyzer.md | 88 + docs/style_guide.md | 37 +- docs/theme/index.hbs | 478 + docs/theme/pagetoc.css | 107 + docs/theme/pagetoc.js | 120 + flake.lock | 326 +- flake.nix | 61 +- hydrajobs/flake-module.nix | 55 +- lib.nix | 117 +- lib/ghaf-modules.nix | 5 +- lib/icons.nix | 101 + lib/launcher.nix | 18 + lib/mk-flash-script/default.nix | 64 +- mk_patches.sh | 10 +- modules/common/boot/systemd-boot-dtb.nix | 38 +- modules/common/common.nix | 3 +- modules/common/default.nix | 7 +- .../development/audio_test/test_file1.mp3 | Bin 0 -> 1362475 bytes modules/common/development/debug-tools.nix | 87 +- modules/common/development/nix.nix | 71 +- .../development/scripts/nvpmodel_check.nix | 37 + .../scripts/perf_test_icicle_kit.nix | 25 + .../scripts/rm_linux_bootmgr_entries.nix | 15 + .../scripts/sysbench_fileio_test.nix | 49 + .../development/scripts/sysbench_test.nix | 46 + modules/common/development/ssh.nix | 33 +- modules/common/development/usb-serial.nix | 36 +- modules/common/firewall/default.nix | 6 +- modules/common/firewall/kernel-modules.nix | 10 +- modules/common/hardware/ax88179_178a.nix | 29 - modules/common/hardware/definition.nix | 143 - .../lenovo-x1/definitions/default.nix | 50 - .../lenovo-x1/definitions/x1-gen10.nix | 32 - .../lenovo-x1/definitions/x1-gen11.nix | 41 - .../lenovo-x1/kernel/guest/test/default.nix | 6 - .../kernel/guest/test/test-configuration.nix | 34 - .../x86_64-generic/kernel/guest/default.nix | 15 - .../x86_64-generic/kernel/hardening.nix | 26 - .../kernel/host/pkvm/test/default.nix | 6 - .../host/pkvm/test/test-configuration.nix | 22 - .../kernel/host/test/default.nix | 6 - .../kernel/host/test/test-configuration.nix | 31 - modules/common/hardware/x86_64-linux.nix | 43 - modules/common/logging/client.nix | 51 + modules/common/logging/default.nix | 40 + modules/common/logging/hw-mac-retrieve.nix | 46 + modules/common/logging/logs-aggregator.nix | 85 + .../common/networking/default.nix | 7 +- modules/common/networking/hosts.nix | 133 + modules/common/profiles/debug.nix | 42 +- modules/common/profiles/default.nix | 1 + modules/common/profiles/host-hardening.nix | 25 + modules/common/profiles/kernel-hardening.nix | 37 + modules/common/profiles/release.nix | 35 +- modules/common/security/default.nix | 3 + .../common/security}/sshkeys.nix | 8 +- modules/common/services/audio.nix | 104 + modules/common/services/default.nix | 14 + modules/common/services/desktop.nix | 149 + modules/common/services/firmware.nix | 18 + modules/common/services/fprint.nix | 68 + modules/common/services/namespaces.nix | 21 + modules/common/services/pdfopen.nix | 65 + modules/common/services/wifi.nix | 54 + modules/common/services/yubikey.nix | 60 + modules/common/systemd/base.nix | 276 +- modules/common/systemd/boot.nix | 69 +- modules/common/systemd/default.nix | 2 +- modules/common/systemd/harden.nix | 63 + .../common/NetworkManager-dispatcher.nix | 151 + .../systemd/hardened-configs/common/dbus.nix | 181 + .../hardened-configs/common/dnsmasq.nix | 151 + .../hardened-configs/common/enable-ksm.nix | 151 + .../hardened-configs/common/firewall.nix | 151 + .../common/generate-shutdown-ramfs.nix | 151 + .../hardened-configs/common/ghaf-session.nix | 151 + .../common/install-microvm-netvm.nix | 151 + .../common/kmod-static-nodes.nix | 151 + .../common/logrotate-checkconf.nix | 151 + .../hardened-configs/common/logrotate.nix | 151 + .../common/microvm-tap-interfaces@.nix | 151 + .../common/microvm-virtiofsd@.nix | 152 + .../hardened-configs/common/microvm@.nix | 151 + .../common/network-local-commands.nix | 151 + .../systemd/hardened-configs/common/nscd.nix | 151 + .../hardened-configs/common/pulseaudio.nix | 154 + .../hardened-configs/common/rtkit-daemon.nix | 151 + .../systemd/hardened-configs/common/seatd.nix | 151 + .../common/systemd-fsck-root.nix | 151 + .../common/systemd-journal-catalog-update.nix | 151 + .../common/systemd-journal-flush.nix | 151 + .../common/systemd-networkd-wait-online.nix | 151 + .../common/systemd-random-seed.nix | 151 + .../common/systemd-remount-fs.nix | 151 + .../common/systemd-rfkill.nix | 151 + .../common/systemd-tmpfiles-clean.nix | 151 + .../common/systemd-tmpfiles-setup-dev.nix | 151 + .../common/systemd-tmpfiles-setup.nix | 151 + .../common/systemd-udev-trigger.nix | 151 + .../hardened-configs/common/systemd-udevd.nix | 151 + .../common/systemd-user-sessions.nix | 151 + .../hardened-configs/common/tpm2-abrmd.nix | 151 + .../common/user-runtime-dir@.nix | 151 + .../hardened-configs/common/vsockproxy.nix | 151 + .../common/wpa_supplicant.nix | 151 + .../release/NetworkManager.nix | 153 + .../hardened-configs/release/audit.nix | 151 + .../systemd/hardened-configs/release/sshd.nix | 161 + .../hardened-configs/release/user@.nix | 151 + .../systemd/hardened-configs/template.nix | 151 + modules/common/tpm2/default.nix | 35 - modules/common/users/accounts.nix | 87 +- modules/common/version/default.nix | 10 +- modules/common/virtualization/docker.nix | 30 +- modules/desktop/default.nix | 1 - modules/desktop/graphics/boot.nix | 47 +- modules/desktop/graphics/default.nix | 6 +- modules/desktop/graphics/demo-apps.nix | 70 +- modules/desktop/graphics/fonts.nix | 15 +- modules/desktop/graphics/ghaf-launcher.nix | 57 +- modules/desktop/graphics/gnome.nix | 45 - modules/desktop/graphics/labwc.config.nix | 296 + modules/desktop/graphics/labwc.nix | 227 +- modules/desktop/graphics/launchers.nix | 39 +- modules/desktop/graphics/login-manager.nix | 74 + modules/desktop/graphics/waybar.config.nix | 204 +- modules/desktop/graphics/weston.ini.nix | 72 - modules/desktop/graphics/weston.nix | 73 - modules/desktop/graphics/window-manager.nix | 100 - modules/desktop/profiles/applications.nix | 34 +- modules/desktop/profiles/graphics.nix | 109 +- modules/desktop/windows-launcher/default.nix | 42 - modules/disko/disko-ab-partitions.nix | 168 + ...basic.nix => disko-basic-partition-v1.nix} | 17 +- modules/disko/disko-basic-postboot.nix | 6 +- modules/disko/disko-zfs-postboot.nix | 38 + modules/disko/flake-module.nix | 15 +- modules/flake-module.nix | 35 +- modules/givc/adminvm.nix | 30 + modules/givc/appvm.nix | 50 + modules/givc/audiovm.nix | 30 + modules/givc/common.nix | 73 + modules/givc/flake-module.nix | 46 + modules/givc/gpiovm.nix | 37 + modules/givc/guivm.nix | 30 + modules/givc/host.nix | 41 + modules/givc/netvm.nix | 37 + modules/hardware/common/default.nix | 12 + modules/hardware/common/devices.nix | 90 + modules/hardware/common/kernel.nix | 90 + modules/hardware/common/qemu.nix | 45 + modules/hardware/common/usb/external.nix | 76 + modules/hardware/common/usb/internal.nix | 93 + modules/hardware/common/usb/vhotplug.nix | 192 + modules/hardware/definition.nix | 360 + .../dell-latitude/dell-latitude-7230.nix | 172 + .../dell-latitude/dell-latitude-7330.nix | 185 + modules/hardware/flake-module.nix | 18 + modules/hardware/laptop.nix | 71 + .../lenovo-x1/definitions/x1-gen10.nix | 150 + .../lenovo-x1/definitions/x1-gen11.nix | 153 + .../lenovo-x1/kernel/guest/test/default.nix | 7 + .../kernel/guest/test/test-configuration.nix | 40 + .../hardware/x86_64-generic/default.nix | 2 + .../configs/ghaf_host_hardened_baseline-x86 | 0 .../kernel/guest/configs/display-gpu.config | 0 .../kernel/guest/configs/guest.config | 0 .../x86_64-generic/kernel/guest/default.nix | 18 + .../x86_64-generic/kernel/hardening.nix | 37 + .../kernel/host/configs/debug.config | 0 .../kernel/host/configs/networking.config | 0 .../kernel/host/configs/usb.config | 0 .../host/configs/user-input-devices.config | 0 .../kernel/host/configs/virtualization.config | 0 .../x86_64-generic/kernel/host/default.nix | 44 +- .../kernel/host/pkvm/default.nix | 27 +- .../kernel/host/pkvm/test/default.nix | 7 + .../host/pkvm/test/test-configuration.nix | 19 + .../kernel/host/test/default.nix | 7 + .../kernel/host/test/test-configuration.nix | 31 + .../hardware/x86_64-generic/modules/tpm2.nix | 36 + .../hardware/x86_64-generic/x86_64-linux.nix | 44 + modules/host/default.nix | 9 +- modules/imx8/default.nix | 6 + modules/imx8/imx8mp-sdimage.nix | 55 + .../agx-gpiovm-passthrough.nix | 8 - .../agx-netvm-wlan-pci-passthrough.nix | 14 +- .../nx-netvm-ethernet-pci-passthrough.nix | 14 +- .../nvidia-jetson-orin/format-module.nix | 4 +- .../nvidia-jetson-orin/jetson-orin.nix | 159 +- .../nvidia-jetson-orin/mk-esp-contents.py | 27 +- modules/jetpack/nvidia-jetson-orin/optee.nix | 3 +- .../nvidia-jetson-orin/ota-utils-fix.nix | 19 +- .../nvidia-jetson-orin/partition-template.nix | 140 +- .../pci-passthrough-common.nix | 14 +- .../jetpack/nvidia-jetson-orin/sdimage.nix | 109 +- .../common/bpmp-virt-common/default.nix | 26 +- .../common/gpio-virt-common/default.nix | 29 +- .../patches/0003-gpio-virt-kernel.patch | 414 +- .../patches/0004-gpio-virt-drivers.patch | 52 +- .../patches/0006-defconfig-kernel.patch | 8481 ----------------- .../host/bpmp-virt-host/default.nix | 14 +- .../host/gpio-virt-host/default.nix | 4 +- .../host/uarta-host/default.nix | 54 +- .../virtualization/overlays/default.nix | 10 +- .../passthrough/uarti-net-vm/default.nix | 44 +- modules/jetpack/profiles/debug.nix | 10 +- modules/jetpack/profiles/default.nix | 6 +- modules/lanzaboote/default.nix | 33 +- .../demo-secure-boot-keys/GUID.license | 3 - modules/microvm/default.nix | 15 - modules/microvm/flake-module.nix | 22 + modules/microvm/networking.nix | 74 +- modules/microvm/power-control.nix | 15 + .../virtualization/microvm/adminvm.nix | 134 + .../microvm/virtualization/microvm/appvm.nix | 375 +- .../virtualization/microvm/audiovm.nix | 164 + .../microvm/common/storagevm.nix | 88 + .../microvm/common/vm-networking.nix | 33 +- .../microvm/{ => dtb}/qemu-gpio-guestvm.dtb | Bin .../microvm/dtb}/qemu-gpio-guestvm.dts | 0 .../microvm/virtualization/microvm/gpiovm.nix | 256 +- .../microvm/virtualization/microvm/guivm.nix | 341 +- .../virtualization/microvm/idsvm/idsvm.nix | 98 + .../microvm/idsvm/mitmproxy/default.nix | 68 + .../mitmproxy-ca/mitmproxy-ca-cert.cer | 20 + .../mitmproxy-ca/mitmproxy-ca-cert.p12 | Bin 0 -> 1015 bytes .../mitmproxy-ca/mitmproxy-ca-cert.pem | 20 + .../mitmproxy/mitmproxy-ca/mitmproxy-ca.p12 | Bin 0 -> 2392 bytes .../mitmproxy/mitmproxy-ca/mitmproxy-ca.pem | 47 + .../mitmproxy-ca/mitmproxy-dhparam.pem | 14 + .../virtualization/microvm/microvm-host.nix | 74 +- .../virtualization/microvm/modules.nix | 170 + .../microvm/virtualization/microvm/netvm.nix | 239 +- modules/polarfire/default.nix | 6 +- modules/polarfire/mpfs-nixos-sdimage.nix | 7 +- modules/reference/appvms/appflowy.nix | 28 + modules/reference/appvms/business.nix | 296 + modules/reference/appvms/chromium.nix | 96 + modules/reference/appvms/comms.nix | 115 + modules/reference/appvms/default.nix | 47 + modules/reference/appvms/gala.nix | 27 + modules/reference/appvms/zathura.nix | 29 + .../personalize/authorizedSshKeys.nix} | 7 +- modules/reference/personalize/default.nix | 3 + modules/reference/personalize/keys.nix | 37 + .../profiles}/default.nix | 10 +- modules/reference/profiles/laptop-x86.nix | 123 + modules/reference/profiles/mvp-user-trial.nix | 67 + modules/reference/programs/chromium.nix | 23 + modules/reference/programs/default.nix | 9 + .../reference/programs/windows-launcher.nix | 47 + modules/reference/programs/zathura.nix | 17 + modules/reference/services/default.nix | 23 + .../dendrite-pinecone/dendrite-config.nix | 34 + .../dendrite-pinecone/dendrite-pinecone.nix | 159 + nix/checks.nix | 51 +- nix/devshell.nix | 76 +- nix/devshell/kernel.nix | 111 +- nix/nixpkgs.nix | 25 +- nix/treefmt.nix | 99 +- overlays/README.md | 18 + .../cross-compilation/chromium/default.nix | 42 - overlays/cross-compilation/default.nix | 12 +- overlays/cross-compilation/edk2/default.nix | 39 - .../element-desktop/default.nix | 22 - .../cross-compilation/jbig2dec/default.nix | 7 - overlays/cross-compilation/libck/default.nix | 16 - .../papirus-icon-theme/default.nix | 9 + .../cross-compilation/pipewire/default.nix | 10 - .../cross-compilation/sysbench/default.nix | 19 - overlays/custom-packages/default.nix | 28 +- .../element-desktop/default.nix | 9 + .../element-desktop/element-main.patch | 91 + .../custom-packages/element-gps/default.nix | 3 + .../custom-packages/element-web/default.nix | 3 + ...against-race-condition-with-messages.patch | 36 + overlays/custom-packages/gtklock/default.nix | 15 + overlays/custom-packages/gtklock/update.patch | 116 + overlays/custom-packages/htop/default.nix | 11 - overlays/custom-packages/labwc/default.nix | 5 +- .../labwc/labwc-colored-borders.patch | 273 +- .../custom-packages/mitmweb-ui/default.nix | 3 + .../networkmanagerapplet/default.nix | 11 - overlays/custom-packages/qemu/default.nix | 24 +- overlays/custom-packages/systemd/default.nix | 27 - .../systemd-timesyncd-disable-nscd.patch | 46 - .../custom-packages/tpm2-pkcs11/default.nix | 4 +- overlays/custom-packages/waybar/default.nix | 16 +- overlays/custom-packages/waypipe/default.nix | 15 +- .../waypipe/waypipe-window-borders.patch | 194 +- overlays/custom-packages/weston/default.nix | 29 - .../weston/weston-backport-workspaces.patch | 807 -- packages/audio-ctrl/default.nix | 29 + packages/dendrite-pinecone/default.nix | 27 + packages/element-gps/default.nix | 12 + packages/element-gps/main.py | 124 + packages/element-gps/setup.py | 15 + packages/element-web/default.nix | 97 + packages/element-web/matrix-react-sdk.patch | 206 + packages/element-web/pin.nix | 11 + packages/flake-module.nix | 80 +- packages/flash/default.nix | 17 + packages/flash/flash.sh | 71 + packages/gala/default.nix | 92 +- packages/ghaf-open/default.nix | 38 + packages/hardware-scan/default.nix | 28 + packages/hardware-scan/hardware-scan.sh | 631 ++ ...1-Workaround-for-a-compilation-issue.patch | 16 + packages/hart-software-services/default.nix | 90 +- packages/icon-pack/default.nix | 49 + packages/installer/default.nix | 30 +- packages/installer/ghaf-installer.sh | 96 +- packages/kernel-hardening-checker/default.nix | 5 +- packages/kernel/default.nix | 114 +- packages/make-checks/default.nix | 69 + packages/mitmweb-ui/default.nix | 51 + packages/nm-launcher/default.nix | 13 +- .../openPdf/default.nix | 21 +- packages/powercontrol/default.nix | 123 +- packages/powercontrol/png-icons.nix | 48 - packages/qemuqmp/default.nix | 26 + packages/ssh-keys-helper/default.nix | 16 + packages/vhotplug/default.nix | 29 + packages/vsockproxy/default.nix | 12 +- packages/wifi-connector/default.nix | 104 - packages/wifi-signal-strength/default.nix | 121 +- packages/windows-launcher/default.nix | 296 +- pyproject.toml | 7 + shell.nix | 14 +- targets/flake-module.nix | 5 +- targets/generic-x86_64/flake-module.nix | 105 +- targets/imx8mp-evk/flake-module.nix | 82 + targets/imx8qm-mek/flake-module.nix | 66 - targets/laptop-hw-scan/flake-module.nix | 49 + targets/laptop/flake-module.nix | 101 + .../laptop/laptop-configuration-builder.nix | 50 + targets/lenovo-x1-installer/flake-module.nix | 109 +- targets/lenovo-x1/appvms/chromium.nix | 67 - targets/lenovo-x1/appvms/default.nix | 12 - targets/lenovo-x1/appvms/gala.nix | 16 - targets/lenovo-x1/appvms/zathura.nix | 16 - targets/lenovo-x1/debugModules.nix | 12 - targets/lenovo-x1/everything.nix | 158 - targets/lenovo-x1/flake-module.nix | 22 - targets/lenovo-x1/getAuthKeysSource.nix | 16 - targets/lenovo-x1/guivmExtraModules.nix | 161 - targets/lenovo-x1/netvmExtraModules.nix | 83 - targets/microchip-icicle-kit/flake-module.nix | 58 +- .../nvidia-jetson-orin/cross-compilation.nix | 4 +- targets/nvidia-jetson-orin/flake-module.nix | 191 +- targets/nvidia-jetson-orin/optee.nix | 141 +- targets/vm/flake-module.nix | 89 +- templates/boilerplate/.gitignore | 10 + templates/boilerplate/default.nix | 31 + templates/boilerplate/flake.nix | 113 + .../boilerplate/hydrajobs/flake-module.nix | 3 + .../boilerplate/modules/flake-module.nix | 10 + .../boilerplate/modules/hardware/default.nix | 152 + templates/boilerplate/nix/checks.nix | 16 + templates/boilerplate/nix/devshell.nix | 34 + templates/boilerplate/nix/flake-module.nix | 10 + templates/boilerplate/nix/nixpkgs.nix | 18 + templates/boilerplate/nix/treefmt.nix | 35 + templates/boilerplate/overlays/README.md | 40 + .../overlays/cross-compilation/default.nix | 8 + .../overlays/custom-packages/default.nix | 9 + .../boilerplate/overlays/flake-module.nix | 10 + .../boilerplate/packages/flake-module.nix | 9 + templates/boilerplate/shell.nix | 31 + .../boilerplate/targets/flake-module.nix | 3 + templates/flake-module.nix | 32 +- templates/modules/default.nix | 23 +- .../targets/aarch64/nvidia/orin-agx/flake.nix | 83 - .../targets/aarch64/nvidia/orin-nx/flake.nix | 83 - templates/targets/aarch64/nxp/imx8/flake.nix | 72 - .../riscv64/microchip/polarfire/flake.nix | 77 - templates/targets/x86_64/generic/flake.nix | 75 - 452 files changed, 22888 insertions(+), 15490 deletions(-) create mode 100644 LICENSES/BSD-2-Clause-Patent.txt create mode 100644 LICENSES/GPL-2.0-only.txt create mode 100644 LICENSES/GPL-2.0-or-later.txt create mode 100644 LICENSES/GPL-3.0-only.txt create mode 100644 LICENSES/LGPL-2.1-or-later.txt create mode 100644 LICENSES/LicenseRef-NvidiaProprietary.txt create mode 100644 LICENSES/WTFPL.txt create mode 100644 REUSE.toml delete mode 100644 assets/ghaf-logo.png delete mode 100644 assets/icons/png/app.png delete mode 100644 assets/icons/png/browser.png delete mode 100644 assets/icons/png/pdf.png delete mode 100644 assets/icons/png/settings.png delete mode 100644 assets/icons/png/windows.png delete mode 100644 assets/icons/svg/app.svg delete mode 100644 assets/icons/svg/browser.svg delete mode 100644 assets/icons/svg/pdf.svg delete mode 100644 assets/icons/svg/settings.svg delete mode 100644 assets/icons/svg/windows.svg delete mode 100644 assets/wallpaper.png delete mode 100644 docs/plugins/mdbook-footnote.nix create mode 100644 docs/src/architecture/adr/idsvm.md create mode 100644 docs/src/img/idsvm.drawio.png create mode 100644 docs/src/ref_impl/hw-config.md create mode 100644 docs/src/ref_impl/idsvm-development.md create mode 100644 docs/src/ref_impl/profiles-config.md create mode 100644 docs/src/ref_impl/systemd-service-config.md create mode 100644 docs/src/release_notes/ghaf-24.06.md create mode 100644 docs/src/troubleshooting/README.md create mode 100644 docs/src/troubleshooting/systemd/early-shell.md create mode 100644 docs/src/troubleshooting/systemd/index.md create mode 100644 docs/src/troubleshooting/systemd/strace.md create mode 100644 docs/src/troubleshooting/systemd/system-log.md create mode 100644 docs/src/troubleshooting/systemd/systemctl.md create mode 100644 docs/src/troubleshooting/systemd/systemd-analyzer.md create mode 100644 docs/theme/index.hbs create mode 100644 docs/theme/pagetoc.css create mode 100644 docs/theme/pagetoc.js create mode 100644 lib/icons.nix create mode 100644 lib/launcher.nix create mode 100644 modules/common/development/audio_test/test_file1.mp3 create mode 100644 modules/common/development/scripts/nvpmodel_check.nix create mode 100644 modules/common/development/scripts/perf_test_icicle_kit.nix create mode 100644 modules/common/development/scripts/rm_linux_bootmgr_entries.nix create mode 100755 modules/common/development/scripts/sysbench_fileio_test.nix create mode 100755 modules/common/development/scripts/sysbench_test.nix delete mode 100644 modules/common/hardware/ax88179_178a.nix delete mode 100644 modules/common/hardware/definition.nix delete mode 100644 modules/common/hardware/lenovo-x1/definitions/default.nix delete mode 100644 modules/common/hardware/lenovo-x1/definitions/x1-gen10.nix delete mode 100644 modules/common/hardware/lenovo-x1/definitions/x1-gen11.nix delete mode 100644 modules/common/hardware/lenovo-x1/kernel/guest/test/default.nix delete mode 100644 modules/common/hardware/lenovo-x1/kernel/guest/test/test-configuration.nix delete mode 100644 modules/common/hardware/x86_64-generic/kernel/guest/default.nix delete mode 100644 modules/common/hardware/x86_64-generic/kernel/hardening.nix delete mode 100644 modules/common/hardware/x86_64-generic/kernel/host/pkvm/test/default.nix delete mode 100644 modules/common/hardware/x86_64-generic/kernel/host/pkvm/test/test-configuration.nix delete mode 100644 modules/common/hardware/x86_64-generic/kernel/host/test/default.nix delete mode 100644 modules/common/hardware/x86_64-generic/kernel/host/test/test-configuration.nix delete mode 100644 modules/common/hardware/x86_64-linux.nix create mode 100644 modules/common/logging/client.nix create mode 100644 modules/common/logging/default.nix create mode 100644 modules/common/logging/hw-mac-retrieve.nix create mode 100644 modules/common/logging/logs-aggregator.nix rename targets/lenovo-x1/releaseModules.nix => modules/common/networking/default.nix (63%) create mode 100644 modules/common/networking/hosts.nix create mode 100644 modules/common/profiles/host-hardening.nix create mode 100644 modules/common/profiles/kernel-hardening.nix create mode 100644 modules/common/security/default.nix rename {targets/lenovo-x1 => modules/common/security}/sshkeys.nix (93%) create mode 100644 modules/common/services/audio.nix create mode 100644 modules/common/services/default.nix create mode 100644 modules/common/services/desktop.nix create mode 100644 modules/common/services/firmware.nix create mode 100644 modules/common/services/fprint.nix create mode 100644 modules/common/services/namespaces.nix create mode 100644 modules/common/services/pdfopen.nix create mode 100644 modules/common/services/wifi.nix create mode 100644 modules/common/services/yubikey.nix create mode 100644 modules/common/systemd/harden.nix create mode 100644 modules/common/systemd/hardened-configs/common/NetworkManager-dispatcher.nix create mode 100644 modules/common/systemd/hardened-configs/common/dbus.nix create mode 100644 modules/common/systemd/hardened-configs/common/dnsmasq.nix create mode 100644 modules/common/systemd/hardened-configs/common/enable-ksm.nix create mode 100644 modules/common/systemd/hardened-configs/common/firewall.nix create mode 100644 modules/common/systemd/hardened-configs/common/generate-shutdown-ramfs.nix create mode 100644 modules/common/systemd/hardened-configs/common/ghaf-session.nix create mode 100644 modules/common/systemd/hardened-configs/common/install-microvm-netvm.nix create mode 100644 modules/common/systemd/hardened-configs/common/kmod-static-nodes.nix create mode 100644 modules/common/systemd/hardened-configs/common/logrotate-checkconf.nix create mode 100644 modules/common/systemd/hardened-configs/common/logrotate.nix create mode 100644 modules/common/systemd/hardened-configs/common/microvm-tap-interfaces@.nix create mode 100644 modules/common/systemd/hardened-configs/common/microvm-virtiofsd@.nix create mode 100644 modules/common/systemd/hardened-configs/common/microvm@.nix create mode 100644 modules/common/systemd/hardened-configs/common/network-local-commands.nix create mode 100644 modules/common/systemd/hardened-configs/common/nscd.nix create mode 100644 modules/common/systemd/hardened-configs/common/pulseaudio.nix create mode 100644 modules/common/systemd/hardened-configs/common/rtkit-daemon.nix create mode 100644 modules/common/systemd/hardened-configs/common/seatd.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-fsck-root.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-journal-catalog-update.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-journal-flush.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-networkd-wait-online.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-random-seed.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-remount-fs.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-rfkill.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-tmpfiles-clean.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-tmpfiles-setup-dev.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-tmpfiles-setup.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-udev-trigger.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-udevd.nix create mode 100644 modules/common/systemd/hardened-configs/common/systemd-user-sessions.nix create mode 100644 modules/common/systemd/hardened-configs/common/tpm2-abrmd.nix create mode 100644 modules/common/systemd/hardened-configs/common/user-runtime-dir@.nix create mode 100644 modules/common/systemd/hardened-configs/common/vsockproxy.nix create mode 100644 modules/common/systemd/hardened-configs/common/wpa_supplicant.nix create mode 100644 modules/common/systemd/hardened-configs/release/NetworkManager.nix create mode 100644 modules/common/systemd/hardened-configs/release/audit.nix create mode 100644 modules/common/systemd/hardened-configs/release/sshd.nix create mode 100644 modules/common/systemd/hardened-configs/release/user@.nix create mode 100644 modules/common/systemd/hardened-configs/template.nix delete mode 100644 modules/common/tpm2/default.nix delete mode 100644 modules/desktop/graphics/gnome.nix create mode 100644 modules/desktop/graphics/labwc.config.nix create mode 100644 modules/desktop/graphics/login-manager.nix delete mode 100644 modules/desktop/graphics/weston.ini.nix delete mode 100644 modules/desktop/graphics/weston.nix delete mode 100644 modules/desktop/graphics/window-manager.nix delete mode 100644 modules/desktop/windows-launcher/default.nix create mode 100644 modules/disko/disko-ab-partitions.nix rename modules/disko/{lenovo-x1-disko-basic.nix => disko-basic-partition-v1.nix} (83%) create mode 100644 modules/disko/disko-zfs-postboot.nix create mode 100644 modules/givc/adminvm.nix create mode 100644 modules/givc/appvm.nix create mode 100644 modules/givc/audiovm.nix create mode 100644 modules/givc/common.nix create mode 100644 modules/givc/flake-module.nix create mode 100644 modules/givc/gpiovm.nix create mode 100644 modules/givc/guivm.nix create mode 100644 modules/givc/host.nix create mode 100644 modules/givc/netvm.nix create mode 100644 modules/hardware/common/default.nix create mode 100644 modules/hardware/common/devices.nix create mode 100644 modules/hardware/common/kernel.nix create mode 100644 modules/hardware/common/qemu.nix create mode 100644 modules/hardware/common/usb/external.nix create mode 100644 modules/hardware/common/usb/internal.nix create mode 100644 modules/hardware/common/usb/vhotplug.nix create mode 100644 modules/hardware/definition.nix create mode 100644 modules/hardware/definitions/dell-latitude/dell-latitude-7230.nix create mode 100644 modules/hardware/definitions/dell-latitude/dell-latitude-7330.nix create mode 100644 modules/hardware/flake-module.nix create mode 100644 modules/hardware/laptop.nix create mode 100644 modules/hardware/lenovo-x1/definitions/x1-gen10.nix create mode 100644 modules/hardware/lenovo-x1/definitions/x1-gen11.nix create mode 100644 modules/hardware/lenovo-x1/kernel/guest/test/default.nix create mode 100644 modules/hardware/lenovo-x1/kernel/guest/test/test-configuration.nix rename modules/{common => }/hardware/x86_64-generic/default.nix (81%) rename modules/{common => }/hardware/x86_64-generic/kernel/configs/ghaf_host_hardened_baseline-x86 (100%) rename modules/{common => }/hardware/x86_64-generic/kernel/guest/configs/display-gpu.config (100%) rename modules/{common => }/hardware/x86_64-generic/kernel/guest/configs/guest.config (100%) create mode 100644 modules/hardware/x86_64-generic/kernel/guest/default.nix create mode 100644 modules/hardware/x86_64-generic/kernel/hardening.nix rename modules/{common => }/hardware/x86_64-generic/kernel/host/configs/debug.config (100%) rename modules/{common => }/hardware/x86_64-generic/kernel/host/configs/networking.config (100%) rename modules/{common => }/hardware/x86_64-generic/kernel/host/configs/usb.config (100%) rename modules/{common => }/hardware/x86_64-generic/kernel/host/configs/user-input-devices.config (100%) rename modules/{common => }/hardware/x86_64-generic/kernel/host/configs/virtualization.config (100%) rename modules/{common => }/hardware/x86_64-generic/kernel/host/default.nix (54%) rename modules/{common => }/hardware/x86_64-generic/kernel/host/pkvm/default.nix (64%) create mode 100644 modules/hardware/x86_64-generic/kernel/host/pkvm/test/default.nix create mode 100644 modules/hardware/x86_64-generic/kernel/host/pkvm/test/test-configuration.nix create mode 100644 modules/hardware/x86_64-generic/kernel/host/test/default.nix create mode 100644 modules/hardware/x86_64-generic/kernel/host/test/test-configuration.nix create mode 100644 modules/hardware/x86_64-generic/modules/tpm2.nix create mode 100644 modules/hardware/x86_64-generic/x86_64-linux.nix create mode 100644 modules/imx8/default.nix create mode 100644 modules/imx8/imx8mp-sdimage.nix delete mode 100644 modules/lanzaboote/demo-secure-boot-keys/GUID.license delete mode 100644 modules/microvm/default.nix create mode 100644 modules/microvm/flake-module.nix create mode 100644 modules/microvm/power-control.nix create mode 100644 modules/microvm/virtualization/microvm/adminvm.nix create mode 100644 modules/microvm/virtualization/microvm/audiovm.nix create mode 100644 modules/microvm/virtualization/microvm/common/storagevm.nix rename modules/microvm/virtualization/microvm/{ => dtb}/qemu-gpio-guestvm.dtb (100%) rename modules/{jetpack-microvm => microvm/virtualization/microvm/dtb}/qemu-gpio-guestvm.dts (100%) create mode 100644 modules/microvm/virtualization/microvm/idsvm/idsvm.nix create mode 100644 modules/microvm/virtualization/microvm/idsvm/mitmproxy/default.nix create mode 100644 modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca-cert.cer create mode 100644 modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca-cert.p12 create mode 100644 modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca-cert.pem create mode 100644 modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca.p12 create mode 100644 modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca.pem create mode 100644 modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-dhparam.pem create mode 100644 modules/microvm/virtualization/microvm/modules.nix create mode 100644 modules/reference/appvms/appflowy.nix create mode 100644 modules/reference/appvms/business.nix create mode 100644 modules/reference/appvms/chromium.nix create mode 100644 modules/reference/appvms/comms.nix create mode 100644 modules/reference/appvms/default.nix create mode 100644 modules/reference/appvms/gala.nix create mode 100644 modules/reference/appvms/zathura.nix rename modules/{common/development/authorized_ssh_keys.nix => reference/personalize/authorizedSshKeys.nix} (72%) create mode 100644 modules/reference/personalize/default.nix create mode 100644 modules/reference/personalize/keys.nix rename modules/{common/hardware => reference/profiles}/default.nix (56%) create mode 100644 modules/reference/profiles/laptop-x86.nix create mode 100644 modules/reference/profiles/mvp-user-trial.nix create mode 100644 modules/reference/programs/chromium.nix create mode 100644 modules/reference/programs/default.nix create mode 100644 modules/reference/programs/windows-launcher.nix create mode 100644 modules/reference/programs/zathura.nix create mode 100644 modules/reference/services/default.nix create mode 100644 modules/reference/services/dendrite-pinecone/dendrite-config.nix create mode 100644 modules/reference/services/dendrite-pinecone/dendrite-pinecone.nix delete mode 100644 overlays/cross-compilation/chromium/default.nix delete mode 100644 overlays/cross-compilation/edk2/default.nix delete mode 100644 overlays/cross-compilation/element-desktop/default.nix delete mode 100644 overlays/cross-compilation/jbig2dec/default.nix delete mode 100644 overlays/cross-compilation/libck/default.nix create mode 100644 overlays/cross-compilation/papirus-icon-theme/default.nix delete mode 100644 overlays/cross-compilation/pipewire/default.nix delete mode 100644 overlays/cross-compilation/sysbench/default.nix create mode 100644 overlays/custom-packages/element-desktop/default.nix create mode 100644 overlays/custom-packages/element-desktop/element-main.patch create mode 100644 overlays/custom-packages/element-gps/default.nix create mode 100644 overlays/custom-packages/element-web/default.nix create mode 100644 overlays/custom-packages/gtklock/auth-guard-against-race-condition-with-messages.patch create mode 100644 overlays/custom-packages/gtklock/default.nix create mode 100644 overlays/custom-packages/gtklock/update.patch delete mode 100644 overlays/custom-packages/htop/default.nix create mode 100644 overlays/custom-packages/mitmweb-ui/default.nix delete mode 100644 overlays/custom-packages/networkmanagerapplet/default.nix delete mode 100644 overlays/custom-packages/systemd/default.nix delete mode 100644 overlays/custom-packages/systemd/systemd-timesyncd-disable-nscd.patch delete mode 100644 overlays/custom-packages/weston/default.nix delete mode 100644 overlays/custom-packages/weston/weston-backport-workspaces.patch create mode 100644 packages/audio-ctrl/default.nix create mode 100644 packages/dendrite-pinecone/default.nix create mode 100644 packages/element-gps/default.nix create mode 100755 packages/element-gps/main.py create mode 100644 packages/element-gps/setup.py create mode 100644 packages/element-web/default.nix create mode 100644 packages/element-web/matrix-react-sdk.patch create mode 100644 packages/element-web/pin.nix create mode 100644 packages/flash/default.nix create mode 100755 packages/flash/flash.sh create mode 100644 packages/ghaf-open/default.nix create mode 100644 packages/hardware-scan/default.nix create mode 100755 packages/hardware-scan/hardware-scan.sh create mode 100644 packages/hart-software-services/0001-Workaround-for-a-compilation-issue.patch create mode 100644 packages/icon-pack/default.nix create mode 100644 packages/make-checks/default.nix create mode 100644 packages/mitmweb-ui/default.nix rename targets/lenovo-x1/openPdf.nix => packages/openPdf/default.nix (63%) delete mode 100644 packages/powercontrol/png-icons.nix create mode 100644 packages/qemuqmp/default.nix create mode 100644 packages/ssh-keys-helper/default.nix create mode 100644 packages/vhotplug/default.nix delete mode 100644 packages/wifi-connector/default.nix create mode 100644 pyproject.toml create mode 100644 targets/imx8mp-evk/flake-module.nix delete mode 100644 targets/imx8qm-mek/flake-module.nix create mode 100644 targets/laptop-hw-scan/flake-module.nix create mode 100644 targets/laptop/flake-module.nix create mode 100644 targets/laptop/laptop-configuration-builder.nix delete mode 100644 targets/lenovo-x1/appvms/chromium.nix delete mode 100644 targets/lenovo-x1/appvms/default.nix delete mode 100644 targets/lenovo-x1/appvms/gala.nix delete mode 100644 targets/lenovo-x1/appvms/zathura.nix delete mode 100644 targets/lenovo-x1/debugModules.nix delete mode 100644 targets/lenovo-x1/everything.nix delete mode 100644 targets/lenovo-x1/flake-module.nix delete mode 100644 targets/lenovo-x1/getAuthKeysSource.nix delete mode 100644 targets/lenovo-x1/guivmExtraModules.nix delete mode 100644 targets/lenovo-x1/netvmExtraModules.nix create mode 100644 templates/boilerplate/.gitignore create mode 100644 templates/boilerplate/default.nix create mode 100644 templates/boilerplate/flake.nix create mode 100644 templates/boilerplate/hydrajobs/flake-module.nix create mode 100644 templates/boilerplate/modules/flake-module.nix create mode 100644 templates/boilerplate/modules/hardware/default.nix create mode 100644 templates/boilerplate/nix/checks.nix create mode 100644 templates/boilerplate/nix/devshell.nix create mode 100644 templates/boilerplate/nix/flake-module.nix create mode 100644 templates/boilerplate/nix/nixpkgs.nix create mode 100644 templates/boilerplate/nix/treefmt.nix create mode 100644 templates/boilerplate/overlays/README.md create mode 100644 templates/boilerplate/overlays/cross-compilation/default.nix create mode 100644 templates/boilerplate/overlays/custom-packages/default.nix create mode 100644 templates/boilerplate/overlays/flake-module.nix create mode 100644 templates/boilerplate/packages/flake-module.nix create mode 100644 templates/boilerplate/shell.nix create mode 100644 templates/boilerplate/targets/flake-module.nix delete mode 100644 templates/targets/aarch64/nvidia/orin-agx/flake.nix delete mode 100644 templates/targets/aarch64/nvidia/orin-nx/flake.nix delete mode 100644 templates/targets/aarch64/nxp/imx8/flake.nix delete mode 100644 templates/targets/riscv64/microchip/polarfire/flake.nix delete mode 100644 templates/targets/x86_64/generic/flake.nix diff --git a/LICENSES/BSD-2-Clause-Patent.txt b/LICENSES/BSD-2-Clause-Patent.txt new file mode 100644 index 000000000..31de6e498 --- /dev/null +++ b/LICENSES/BSD-2-Clause-Patent.txt @@ -0,0 +1,19 @@ +Copyright (c) + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by: + +(a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of contributors, in source or binary form) alone; or + +(b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution. + +Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this license, whether expressly, by implication, estoppel or otherwise. + +DISCLAIMER + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSES/GPL-2.0-only.txt b/LICENSES/GPL-2.0-only.txt new file mode 100644 index 000000000..17cb28643 --- /dev/null +++ b/LICENSES/GPL-2.0-only.txt @@ -0,0 +1,117 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + + c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author + + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. + +signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice diff --git a/LICENSES/GPL-2.0-or-later.txt b/LICENSES/GPL-2.0-or-later.txt new file mode 100644 index 000000000..17cb28643 --- /dev/null +++ b/LICENSES/GPL-2.0-or-later.txt @@ -0,0 +1,117 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + + c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author + + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. + +signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice diff --git a/LICENSES/GPL-3.0-only.txt b/LICENSES/GPL-3.0-only.txt new file mode 100644 index 000000000..f6cdd22a6 --- /dev/null +++ b/LICENSES/GPL-3.0-only.txt @@ -0,0 +1,232 @@ +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS + +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + +7. Additional Terms. +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + +8. Termination. +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + +14. Revised Versions of this License. +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . + +The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . diff --git a/LICENSES/LGPL-2.1-or-later.txt b/LICENSES/LGPL-2.1-or-later.txt new file mode 100644 index 000000000..c6487f4fd --- /dev/null +++ b/LICENSES/LGPL-2.1-or-later.txt @@ -0,0 +1,176 @@ +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. + +GNU LESSER GENERAL PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. + +When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: + + a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. + + e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. + + b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). + +To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + one line to give the library's name and an idea of what it does. + Copyright (C) year name of author + + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in +the library `Frob' (a library for tweaking knobs) written +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 +Ty Coon, President of Vice +That's all there is to it! diff --git a/LICENSES/LicenseRef-NvidiaProprietary.txt b/LICENSES/LicenseRef-NvidiaProprietary.txt new file mode 100644 index 000000000..63ac26db7 --- /dev/null +++ b/LICENSES/LicenseRef-NvidiaProprietary.txt @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2013-2021, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA CORPORATION and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA CORPORATION is strictly prohibited. + * + * This product incorporates software provided under the following terms: + * + * --------------------------------------------------------------------------- + * + * Copyright (c) 2008-2015 Travis Geiselbrecht + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * --------------------------------------------------------------------------- + * + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * Used under the BSD license: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * --------------------------------------------------------------------------- + * + * The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: + * + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * LLVM Exceptions to the Apache 2.0 License: + * + * As an exception, if, as a result of your compiling your source code, + * portions of this Software are embedded into an Object form of such source + * code, you may redistribute such embedded portions in such Object form + * without complying with the conditions of Sections 4(a), 4(b) and 4(d) of + * the License. + * + * In addition, if you combine or link compiled forms of this Software with + * software that is licensed under the GPLv2 ("Combined Software") and if a + * court of competent jurisdiction determines that the patent provision + * (Section 3), the indemnity provision (Section 9) or other Section of the + * License conflicts with the conditions of the GPLv2, you may retroactively + * and prospectively choose to deem waived or otherwise exclude such + * Section(s) of the License, but only in their entirety and only with + * respect to the Combined Software. + * + * --------------------------------------------------------------------------- + */ diff --git a/LICENSES/WTFPL.txt b/LICENSES/WTFPL.txt new file mode 100644 index 000000000..8b1a9d818 --- /dev/null +++ b/LICENSES/WTFPL.txt @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md index 64d228f63..e4e29a696 100644 --- a/README.md +++ b/README.md @@ -36,18 +36,13 @@ See the documentation overview under [README-docs.md](./docs/README-docs.md). Other repositories that are a part of the Ghaf project: * [sbomnix](https://github.com/tiiuae/sbomnix): a utility that generates SBOMs given Nix derivations or out paths -* [ghaf-infra](https://github.com/tiiuae/ghaf-infra), [ci-public](https://github.com/tiiuae/ci-public), [ci-test-automation](https://github.com/tiiuae/ci-test-automation), [ghafscan](https://github.com/tiiuae/ghafscan): CI/CD related files +* [ghaf-infra](https://github.com/tiiuae/ghaf-infra), [ci-test-automation](https://github.com/tiiuae/ci-test-automation), [ghafscan](https://github.com/tiiuae/ghafscan): CI/CD related files * [ghaf-installation-wizard](https://github.com/tiiuae/ghaf-installation-wizard): helps you install Ghaf for the first time ## Build System -Ghaf images are built and tested by our continuous integration system. For more information on a general process, see [Continuous Integration and Distribution](./docs/src/scs/ci-cd-system.md). - -Targets: -Hydra builders on x86 servers: -Disk images successfully built with Hydra are published to . -Build results: +Ghaf images are built and tested by our continuous integration system. For more information on a general process, see [Continuous Integration and Distribution](https://tiiuae.github.io/ghaf/scs/ci-cd-system.html). ## Contributing diff --git a/REUSE.toml b/REUSE.toml new file mode 100644 index 000000000..bc599fa34 --- /dev/null +++ b/REUSE.toml @@ -0,0 +1,127 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 + +version = 1 + +SPDX-PackageName = "ghaf" +SPDX-PackageSupplier = "Technology Innovation Institute " +SPDX-PackageDownloadLocation = "https://github.com/tiiuae/ghaf" + +[[annotations]] +SPDX-License-Identifier = "Apache-2.0" +SPDX-FileCopyrightText = "2022-2024 TII (SSRC) and the Ghaf contributors" +precedence = "closest" +path = [ + "flake.lock", ".version", + "assets/**/*.png", "assets/**/*.svg", + "modules/common/development/audio_test/test_file1.mp3", + "modules/hardware/x86_64-generic/kernel/configs/ghaf_host_hardened_baseline-x86", + "modules/jetpack/ghaf_host_hardened_baseline-jetson-orin", + "modules/lanzaboote/demo-secure-boot-keys/**/*", + "modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/*", +] + +[[annotations]] +SPDX-License-Identifier = "CC-BY-SA-4.0" +SPDX-FileCopyrightText = "2022-2024 TII (SSRC) and the Ghaf contributors" +precedence = "closest" +path = [ + "docs/**/*.svg", "docs/**/*.png", +] + +# External code + +[[annotations]] +# See https://github.com/qemu/qemu/blob/master/LICENSE +# Our changes affects the GPL-2.0+ parts only. +SPDX-License-Identifier = "GPL-2.0-or-later" +SPDX-FileCopyrightText = [ + "Fabrice Bellard and the QEMU team", + "Copyright (c) 2021-2022 Canokeys.org ", + "Written by Hongren (Zenithal) Zheng ", + "Copyright (c) 2019 Janus Technologies, Inc. (http://janustech.com)", + "Copyright (C) 2008-2010 Kevin O'Connor ", + "Copyright (C) 2006 Fabrice Bellard", + "Copyright (C) 2013 Red Hat Inc", +] +path = [ + "overlays/custom-packages/qemu/*.patch", + "modules/jetpack/nvidia-jetson-orin/virtualization/host/bpmp-virt-host/overlays/qemu/patches/0001-qemu-v8.1.3_bpmp-virt.patch" +] + +[[annotations]] +SPDX-License-Identifier = "GPL-2.0-only" +SPDX-FileCopyrightText = "labwc contributors" +path = "overlays/custom-packages/labwc/*.patch" + +[[annotations]] +# gtklock doesn't specify if later versions is allowed +SPDX-License-Identifier = "GPL-3.0-only" +SPDX-FileCopyrightText = [ + "Copyright (c) 2022 Kenny Levinsen, Jovan Lanik, Erik Reider, Melih Darcan, Bhaskar Khoraja", + "Copyright (c) 2022 Zephyr Lykos" +] +path = "overlays/custom-packages/gtklock/*.patch" + +[[annotations]] +SPDX-License-Identifier = "LGPL-2.1-or-later" +SPDX-FileCopyrightText = "systemd contributors" +path = "modules/common/systemd/systemd-boot-double-dtb-buffer-size.patch" + +[[annotations]] +SPDX-License-Identifier = "MIT" +SPDX-FileCopyrightText = "Copyright © 2019 Manuel Stoeckl" +path = "overlays/custom-packages/waypipe/waypipe-window-borders.patch" + +[[annotations]] +SPDX-License-Identifier = "Apache-2.0" +SPDX-FileCopyrightText = "Copyright 2023 The Matrix.org Foundation C.I.C." +path = "packages/element-web/*.patch" + +[[annotations]] +SPDX-License-Identifier = "Apache-2.0" +SPDX-FileCopyrightText = [ + "Copyright 2016 Aviral Dasgupta", + "Copyright 2016 OpenMarket Ltd", + "Copyright 2017, 2019 Michael Telatynski <7t3chguy@gmail.com>", + "Copyright 2018 - 2021 New Vector Ltd", +] +path = "overlays/custom-packages/element-desktop/element-main.patch" + +[[annotations]] +SPDX-License-Identifier = "GPL-2.0-only" +SPDX-FileCopyrightText = [ + "Copyright (C) 2013 - Virtual Open Systems", + "Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.", + "Copyright (c) 2018, NVIDIA CORPORATION.", + "Copyright (C) 2006 Qumranet, Inc.", + "Copyright 2010 Red Hat, Inc. and/or its affiliates.", + "2022-2024 TII (SSRC) and the Ghaf contributors", +] +path = [ + "modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/patches/*.patch", + "modules/jetpack/nvidia-jetson-orin/virtualization/host/bpmp-virt-host/patches/*.patch", + "modules/jetpack-microvm/*.patch", + "modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/patches/net_vm_dtb_with_uarti.patch", + "modules/common/virtualization/pkvm/0001-pkvm-enable-pkvm-on-intel-x86-6.1-lts.patch", +] + +[[annotations]] +SPDX-License-Identifier = "BSD-2-Clause-Patent" +SPDX-FileCopyrightText = "Copyright (c) 2021-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved." +path = "modules/jetpack/nvidia-jetson-orin/edk2-nvidia-always-reset-display.patch" + +[[annotations]] +SPDX-License-Identifier = "LicenseRef-NvidiaProprietary" +SPDX-FileCopyrightText = "Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved." +path = [ + "modules/jetpack/nvidia-jetson-orin/tegra2-mb2-bct-scr.patch", +] + + +[[annotations]] +SPDX-License-Identifier = "MIT" +SPDX-FileCopyrightText = "Copyright 2019-2021 Microchip Corporation." +path = [ + "packages/hart-software-services/0001-Workaround-for-a-compilation-issue.patch", +] diff --git a/assets/ghaf-logo.png b/assets/ghaf-logo.png deleted file mode 100644 index f36f50389ba7ab92edaba3923ecd04ee7ee13c41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6066 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 diff --git a/assets/icons/png/app.png b/assets/icons/png/app.png deleted file mode 100644 index 86e64c069e9c376c9dbcf0cdb793cdc37d1b504a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 857 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 diff --git a/assets/icons/png/windows.png b/assets/icons/png/windows.png deleted file mode 100644 index 78442b82d0b1308126197f71c4347b1bdb464eff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gj5uPrNAr*{orzr9f&I3J13{@mMd*GG6<_?pQ0EBHNGSYZ}5+zBw_nipU7u`1()iRJVn5 smdKI;Vst03+5f*8l(j diff --git a/assets/icons/svg/app.svg b/assets/icons/svg/app.svg deleted file mode 100644 index 56f137698..000000000 --- a/assets/icons/svg/app.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/icons/svg/browser.svg b/assets/icons/svg/browser.svg deleted file mode 100644 index d69e35433..000000000 --- a/assets/icons/svg/browser.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/assets/icons/svg/pdf.svg b/assets/icons/svg/pdf.svg deleted file mode 100644 index ec974128c..000000000 --- a/assets/icons/svg/pdf.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/icons/svg/settings.svg b/assets/icons/svg/settings.svg deleted file mode 100644 index 362ba2f3e..000000000 --- a/assets/icons/svg/settings.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/assets/icons/svg/windows.svg b/assets/icons/svg/windows.svg deleted file mode 100644 index 16a385c0a..000000000 --- a/assets/icons/svg/windows.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/assets/wallpaper.png b/assets/wallpaper.png deleted file mode 100644 index 4bd7b6069d28a6f0e43c5a05442311ee7abd2e85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16129 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 $out/src/ref_impl/modules_options.md ''; in - # TODO Change this, runCommandLocal is not intended for longer running processes - runCommandLocal "ghaf-doc" +# TODO Change this, runCommandLocal is not intended for longer running processes +runCommandLocal "ghaf-doc" { - nativeBuildInputs = let - footnote = callPackage ./plugins/mdbook-footnote.nix {}; - in [mdbook footnote]; + nativeBuildInputs = [ + mdbook + mdbook-footnote + mdbook-alerts + ]; src = combinedSrc; # set the package Meta info - meta = with lib; { + meta = { description = "Ghaf Documentation"; # TODO should we Only push docs from one Architecture? platforms = [ @@ -48,6 +50,7 @@ in "aarch64-linux" ]; }; - } '' + } + '' ${mdbook}/bin/mdbook build -d $out $src '' diff --git a/docs/plugins/mdbook-footnote.nix b/docs/plugins/mdbook-footnote.nix deleted file mode 100644 index 6e27b3729..000000000 --- a/docs/plugins/mdbook-footnote.nix +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: CC-BY-SA-4.0 -{ - fetchFromGitHub, - rustPlatform, -}: -rustPlatform.buildRustPackage rec { - pname = "mdbook-footnote"; - version = "0.1.1"; - - src = fetchFromGitHub { - owner = "daviddrysdale"; - repo = "mdbook-footnote"; - rev = "refs/tags/v${version}"; - sha256 = "sha256-WUMgm1hwsU9BeheLfb8Di0AfvVQ6j92kXxH2SyG3ses="; - }; - - cargoHash = "sha256-Ig+uVCO5oHIkkvFsKiBiUFzjUgH/Pydn4MVJHb2wKGc="; -} -# TODO upstream this to nixpkgs - diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 09712b350..ef596c0ad 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -14,6 +14,7 @@ - [Architecture Decision Records](architecture/adr.md) - [Minimal Host](architecture/adr/minimal-host.md) - [Networking VM](architecture/adr/netvm.md) + - [Intrusion Detection System VM](architecture/adr/idsvm.md) - [Platform Bus for Rust VMM](architecture/adr/platform-bus-passthrough-support.md) - [Hardening](architecture/hardening.md) - [Secure Boot](architecture/secureboot.md) @@ -29,7 +30,11 @@ - [Installer](ref_impl/installer.md) - [Cross-Compilation](ref_impl/cross_compilation.md) - [Creating Application VM](ref_impl/creating_appvm.md) - - [labWC Desktop Environment](ref_impl/labwc.md) + - [Hardware Configuration](ref_impl/hw-config.md) + - [Profiles Configuration](ref_impl/profiles-config.md) + - [labwc Desktop Environment](ref_impl/labwc.md) + - [IDS VM Further Development](ref_impl/idsvm-development.md) + - [systemd Service Hardening](ref_impl/systemd-service-config.md) - [Ghaf as Library: Templates](ref_impl/ghaf-based-project.md) - [Example Project](ref_impl/example_project.md) - [Modules Options](ref_impl/modules_options.md) @@ -55,6 +60,7 @@ - [Public Key Infrastructure](scs/pki.md) - [Security Fix Automation](scs/ghaf-security-fix-automation.md) - [Release Notes](release_notes/release_notes.md) + - [Release ghaf-24.06](release_notes/ghaf-24.06.md) - [Release ghaf-24.03](release_notes/ghaf-24.03.md) - [Release ghaf-23.12](release_notes/ghaf-23.12.md) - [Release ghaf-23.09](release_notes/ghaf-23.09.md) @@ -66,7 +72,6 @@ - [Showcases](scenarios/showcases.md) - [Running Windows VM on Ghaf](scenarios/run_win_vm.md) - [Running Cuttlefish on Ghaf](scenarios/run_cuttlefish.md) -- [Build Your Environment]() ----------- diff --git a/docs/src/appendices/glossary.md b/docs/src/appendices/glossary.md index a50fd266d..9b48d7d9b 100644 --- a/docs/src/appendices/glossary.md +++ b/docs/src/appendices/glossary.md @@ -56,6 +56,7 @@ Source: ### CI/CD _Continuous Integration and Continuous Delivery is a Ghaf software development lifecycle. Continuous Integration refers to regularly integrating code changes into a shared repository, where they are automatically tested and verified. Continuous Delivery—software is released in short iterations._ +> [!NOTE] > Currently, Continuous Deployment is not set up. Continuous Deployment—code is deployed to customers automatically. ### SSRC @@ -86,6 +87,10 @@ Source: [NVIDIA Orin Series System-on-Chip, Technical Reference Manual, Version: _A board support package is a collection of software used to boot and run the embedded system._ +### CA + +_A certificate authority or certification authority is an entity that stores, signs, issues digital certificates, and bind them to cryptographic keys._ + ### DHCP _The Dynamic Host Configuration Protocol is a network protocol that automatically sets IP addresses and other attributes to enable information transfer between network nodes._ @@ -194,6 +199,10 @@ _A stock keeping unit, is a unique code used by sellers to identify and track pr _A system on chip, a microchip that contains the necessary electronic circuits for a fully functional system on a single integrated circuit (IC)._ +### SPKI + +_simple public-key infrastructure_ + ### SSD _solid-state drive_ @@ -301,3 +310,6 @@ Source: _Supply chain Levels for Software Artifacts is a security framework, a check-list of standards and controls to prevent tampering, improve integrity, and secure packages and infrastructure in your projects, businesses or enterprises._ Source: + +[Back to Top ⏫](./glossary.md#glossary) +--- diff --git a/docs/src/architecture/adr.md b/docs/src/architecture/adr.md index cfd762442..559ac7bbf 100644 --- a/docs/src/architecture/adr.md +++ b/docs/src/architecture/adr.md @@ -12,8 +12,9 @@ The Ghaf platform decision log: | Decision Record | Status | | -------- | ----------- | | [Minimal Host](../architecture/adr/minimal-host.md) | Proposed. | -| [netvm—Networking Virtual Machine](../architecture/adr/netvm.md) | Proposed, partially implemented for development and testing. | -| [Platform Bus for RustVMM](../architecture/adr/platform-bus-passthrough-support.md) | Proposed, WIP. | +| [Networking VM](../architecture/adr/netvm.md) | Proposed, partially implemented for development and testing. | +| [IDS VM](../architecture/adr/idsvm.md) | Proposed, partially implemented for development and testing. | +| [Platform Bus for Rust VMM](../architecture/adr/platform-bus-passthrough-support.md) | Proposed, WIP. | To create an architectural decision proposal, open [a pull request](https://github.com/tiiuae/ghaf/blob/main/CONTRIBUTING.md#contributing-documentation) and use the [decision record template](https://github.com/tiiuae/ghaf/blob/main/docs/src/architecture/adr/template.md). Contributions to the Ghaf architecture decisions are welcome. diff --git a/docs/src/architecture/adr/idsvm.md b/docs/src/architecture/adr/idsvm.md new file mode 100644 index 000000000..754acd9ae --- /dev/null +++ b/docs/src/architecture/adr/idsvm.md @@ -0,0 +1,37 @@ + + +# Intrusion Detection System Virtual Machine + + +## Status + +Proposed, partially implemented for development and testing. + +Intrusion Detection VM (IDS VM) reference declaration will be available at [microvm/idsvm.nix](https://github.com/tiiuae/ghaf/blob/main/modules/microvm/virtualization/microvm/idsvm/idsvm.nix). + + +## Context + +Ghaf's high-level design target is to secure a monolithic OS by modularizing the OS to networked VMs. The key security target is to detect intrusions by analyzing the network traffic in the internal network of the OS. + + +## Decision + +The main goal is to have a networking entity in Ghaf's internal network so that all network traffic goes through that entity. Traffic then can be analyzed to detect possible intrusions in inter VM communication and outgoing network traffic (from VM to the Internet). This goal is achieved by introducing a dedicated VM and routing all networking from other VMs to go through it. Then it is possible to use various IDS software solutions in IDS VM to detect possible suspicious network activities. + +![Scope!](../../img/idsvm.drawio.png "IDS VM Solution") + + +## Consequences + +A dedicated IDS VM provides a single checkpoint to detect intrusions and anomalies in the internal network of the OS and to initiate required countermeasures. + +Routing and analyzing the network traffic in a separate VM will reduce network performance. + + +## References + +[IDS VM Further Development](../../ref_impl/idsvm-development.md) diff --git a/docs/src/architecture/adr/minimal-host.md b/docs/src/architecture/adr/minimal-host.md index d5645b5ec..eba393962 100644 --- a/docs/src/architecture/adr/minimal-host.md +++ b/docs/src/architecture/adr/minimal-host.md @@ -85,8 +85,8 @@ No networking may have impact on how the guest-to-guest inter virtual machine co ### No graphics (MH04) -Ghaf minimal host profile for release target has no graphics. Graphics will be compartmentalized to GUIVM. -All graphics and display output related components and dependencies, including kernel drivers, must be removed from kernel configuration. Those are to be passed through to GUIVM. +Ghaf minimal host profile for release target has no graphics. Graphics will be compartmentalized to GUI VM. +All graphics and display output related components and dependencies, including kernel drivers, must be removed from kernel configuration. Those are to be passed through to GUI VM. ### No getty (MH05) diff --git a/docs/src/architecture/adr/netvm.md b/docs/src/architecture/adr/netvm.md index fecc29959..573f5e0a6 100644 --- a/docs/src/architecture/adr/netvm.md +++ b/docs/src/architecture/adr/netvm.md @@ -3,45 +3,50 @@ SPDX-License-Identifier: CC-BY-SA-4.0 --> -# netvm—Networking Virtual Machine +# Networking Virtual Machine + ## Status Proposed, partially implemented for development and testing. -*netvm* reference declaration is available at [netvm/default.nix](https://github.com/tiiuae/ghaf/blob/main/microvmConfigurations/netvm/default.nix). +Networking VM (Net VM) reference declaration is available at [microvm/netvm.nix](https://github.com/tiiuae/ghaf/blob/main/modules/microvm/virtualization/microvm/netvm.nix). + ## Context -Ghaf high-level design target is to secure a monolithic OS by modularizing the OS to networked VMs. The key security target is to not expose the trusted host directly to the Internet. This isolates the attack surface from the Internet to *netvm*. +Ghaf's high-level design target is to secure a monolithic OS by modularizing the OS to networked VMs. The key security target is to not expose the trusted host directly to the Internet. This isolates the attack surface from the Internet to Net VM. The following context diagram illustrates development and secure scenarios: -![Scope!](../../img/netvm.drawio.png "netvm Context") +![Scope!](../../img/netvm.drawio.png "Net VM Context") **Left**: An insecure development scenario. The host is directly connected to the Internet, and the network is bridged from the host to other parts of the system. -**Right**: A secure scenario. The network is passed through to *netvm* and routed to other parts of the system. +**Right**: A secure scenario. The network is passed through to Net VM and routed to other parts of the system. + ## Decision -The development scenario simplifies the target system network access and configuration. This ADR proposes the development *netvm* configuration is maintained to support system development. +The development scenario simplifies the target system network access and configuration. This ADR proposes the development Net VM configuration is maintained to support system development. -The secure scenario is proposed to be implemented with the use of passthrough to DMA and remap the host physical network interface card (PHY NIC) to *netvm*. This cannot be generalized for all hardware targets as it requires: -- Low-level device tree configuration for bootloader and host (at least on platform NIC). -- VMM host user space NIC bus mapping from the host to *netvm*. -- Native network interface driver (not virtual) in *netvm*. Native driver is bound the vendor BSP supported kernel version. +The secure scenario is proposed to be implemented with the use of passthrough to DMA and remap the host physical network interface card (PHY NIC) to Net VM. This cannot be generalized for all hardware targets as it requires: + +* Low-level device tree configuration for bootloader and host (at least on platform NIC). +* VMM host user space NIC bus mapping from the host to Net VM. +* Native network interface driver (not virtual) in Net VM. Native driver is bound the vendor BSP supported kernel version. These depend on the hardware setup. The proposed target setup is that the passthrough network device(s) are implemented as declarative nix-modules for easier user hardware-specific configuration. In practice, a user may configure the declaration of a PCI or USB network card that is available to the available hardware setup. -*netvm* will provide: -- dynamic network configuration: - - A DHCP server for *netvm* to provide IP addresses for the other parts of the system, both static and dynamic. - - Routing from *netvm* to the Internet and/or inter VM. +Net VM will provide a dynamic network configuration: + +* A DHCP server for Net VM to provide IP addresses for the other parts of the system, both static and dynamic. +* Routing from Net VM to the Internet and/or Inter VM. For common reference hardware with platform NIC, the configured modules for network interface passthrough are provided. For more information, see [i.MX 8QM Ethernet Passthrough](https://tiiuae.github.io/ghaf/research/passthrough/ethernet.html). -Details of other network components, such as default firewall rules, DHCP (static and dynamic client addresses), routing, reverse proxies and security monitoring are to be described in their respective architecture decision records. In this context, these are illustrated in the context diagram on the right side of the *netvm* network interface driver. +Details of other network components, such as default firewall rules, DHCP (static and dynamic client addresses), routing, reverse proxies and security monitoring are to be described in their respective architecture decision records. In this context, these are illustrated in the context diagram on the right side of the Net VM network interface driver. + ## Consequences diff --git a/docs/src/architecture/adr/platform-bus-passthrough-support.md b/docs/src/architecture/adr/platform-bus-passthrough-support.md index 15b032704..45f411d9e 100644 --- a/docs/src/architecture/adr/platform-bus-passthrough-support.md +++ b/docs/src/architecture/adr/platform-bus-passthrough-support.md @@ -14,6 +14,7 @@ Proposed, work in progress. This ADR is a work-in-progress note for Ghaf bus passthrough implementation that will support rust-vmm-based hypervisors. +> [!NOTE] > *rust-vmm* is an open-source project that empowers the community to build custom Virtual Machine Monitors (VMMs) and hypervisors. For more information, see . It is crucial to have bus devices passthrough support for ARM-based hardware as the bus is mainly used to connect the peripherals. Nowadays, the only hypervisor with some support for Platform bus is QEMU but the code is dated 2013 and not frequently used. diff --git a/docs/src/architecture/adr/template.md b/docs/src/architecture/adr/template.md index a53fd0207..0630d1e09 100644 --- a/docs/src/architecture/adr/template.md +++ b/docs/src/architecture/adr/template.md @@ -3,19 +3,17 @@ SPDX-License-Identifier: CC-BY-SA-4.0 --> -# Decision record template - +# Decision Record Template -This is the template for managing the ADR files. - -In each ADR file, write these sections: +This is the template[^note1] for managing the ADR files. Use the following sections in each ADR file: # Title + ## Status -What is the status: proposed, accepted, rejected, deprecated, superseded, etc.? +What is the status? *Proposed*, *Accepted*, *Rejected*, *Deprecated*, *Superseded*, etc. ## Context @@ -31,3 +29,6 @@ What is the change that we are proposing and/or doing? ## Consequences What becomes easier or more difficult to do because of this change? + + +[^note1]: This template is based on a [template by Michael Nygard](https://github.com/joelparkerhenderson/architecture-decision-record/tree/main/locales/en/templates/decision-record-template-by-michael-nygard). For more suggestions on writing good ADRs, see the [Architecture decision record (ADR)](https://github.com/joelparkerhenderson/architecture-decision-record/tree/main?tab=readme-ov-file#suggestions-for-writing-good-adrs) public repository. diff --git a/docs/src/architecture/architecture.md b/docs/src/architecture/architecture.md index 727ad34f6..7ab41220d 100644 --- a/docs/src/architecture/architecture.md +++ b/docs/src/architecture/architecture.md @@ -22,5 +22,8 @@ The Ghaf Platform components are used in reference configurations to build image - [Architecture Decision Records](./adr.md) - [Minimal Host](./adr/minimal-host.md) - [Networking VM](./adr/netvm.md) + - [Intrusion Detection System VM](./adr/idsvm.md) - [Platform Bus for Rust VMM](./adr/platform-bus-passthrough-support.md) +- [Hardening](./hardening.md) +- [Secure Boot](./secureboot.md) - [Stack](./stack.md) \ No newline at end of file diff --git a/docs/src/architecture/hardening.md b/docs/src/architecture/hardening.md index 198e81335..75243a855 100644 --- a/docs/src/architecture/hardening.md +++ b/docs/src/architecture/hardening.md @@ -19,13 +19,13 @@ NixOS provides several mechanisms to customize the kernel. The main methods are: * [Declaring kernel command line parameters](https://nixos.wiki/wiki/Linux_kernel#Custom_kernel_commandline): [usage in Ghaf](https://github.com/search?q=repo%3Atiiuae%2Fghaf%20kernelparams&type=code). * [Declaring kernel custom configuration](https://nixos.org/manual/nixos/stable/#sec-linux-config-customizing): [usage in Ghaf](https://github.com/tiiuae/ghaf/blob/main/modules/host/kernel.nix). - + Example of entering the kernel development shell to customize the `.config` and build it: ``` ~/ghaf $ nix develop .#devShells.x86_64-linux.kernel-x86 ... - [ghaf-kernel-devshell:~/ghaf/linux-6.6.7]$ cp ../modules/common/hardware/x86_64-generic/kernel/configs/ghaf_host_hardened_baseline .config + [ghaf-kernel-devshell:~/ghaf/linux-6.6.7]$ cp ../modules/hardware/x86_64-generic/kernel/configs/ghaf_host_hardened_baseline .config [ghaf-kernel-devshell:~/ghaf/linux-6.6.7]$ make menuconfig ... [ghaf-kernel-devshell:~/ghaf/linux-6.6.7]$ make -j$(nproc) @@ -42,8 +42,8 @@ NixOS provides several mechanisms to customize the kernel. The main methods are: * [Validating with kernel hardening checker](https://github.com/a13xp0p0v/kernel-hardening-checker): ``` - [ghaf-kernel-devshell:~/ghaf/linux-6.6.7]$ cp ../modules/common/hardware/x86_64-generic/kernel/configs/ghaf_host_hardened_baseline .config - [ghaf-kernel-devshell:~/ghaf/linux-6.6.7]$ HS=../modules/common/hardware/x86_64-generic/kernel/host/configs GS=../modules/common/hardware/x86_64-generic/kernel/guest/configs + [ghaf-kernel-devshell:~/ghaf/linux-6.6.7]$ cp ../modules/hardware/x86_64-generic/kernel/configs/ghaf_host_hardened_baseline .config + [ghaf-kernel-devshell:~/ghaf/linux-6.6.7]$ HS=../modules/hardware/x86_64-generic/kernel/host/configs GS=../modules/hardware/x86_64-generic/kernel/guest/configs [ghaf-kernel-devshell:~/ghaf/linux-6.6.7]$ ./scripts/kconfig/merge_config.sh .config $HS/virtualization.config $HS/networking.config $HS/usb.config $HS/user-input-devices.config $HS/debug.config $GS/guest.config $GS/display-gpu.config [ghaf-kernel-devshell:~/ghaf/linux-6.6.7]$ kernel-hardening-checker -c .config [+] Kconfig file to check: .config @@ -74,7 +74,7 @@ The host kernel runs on bare metal. The kernel is provided either with Linux ups The host kernel hardening is based on Linux `make tinyconfig`. The default `tinyconfig` fails to assertions on NixOS without modifications. Assertions are fixed in the `ghaf_host_hardened_baseline` Linux configuration under Ghaf -`modules/common/hardware/x86_64-generic/kernel/configs`. Resulting baseline +`modules/hardware/x86_64-generic/kernel/configs`. Resulting baseline kernel configuration is generic for x86_64 hardware architecture devices. In addition, NixOS (Ghaf baseline dependency) requires several kernel modules that are added to the config or ignored with `allowMissing = true`. As of now, the kernel builds and early boots on Lenovo X1. diff --git a/docs/src/architecture/variants.md b/docs/src/architecture/variants.md index 3b9e50146..e3c44b0e6 100644 --- a/docs/src/architecture/variants.md +++ b/docs/src/architecture/variants.md @@ -8,10 +8,10 @@ The main scope of the Ghaf platform is edge virtualization. However, to support modular development and testing of the platform, variants are supported with the following definitions: * `Default` - A default variant. Supports [minimal host](./adr/minimal-host.md), GUI VM[^note] and [netvm](./adr/netvm.md). May host other VMs. For more information, see [Stack](./stack.md). + A default variant. Supports [minimal host](./adr/minimal-host.md), GUI VM[^note1] and [netvm](./adr/netvm.md). May host other VMs. For more information, see [Stack](./stack.md). * `Headless` - A variant with [minimal host](./adr/minimal-host.md) and [netvm](./adr/netvm.md). May host other VMs but does not have GUI VM or graphics stack on a host. + A variant with [minimal host](./adr/minimal-host.md) and [netvm](./adr/netvm.md). May host other VMs but does not have a GUI VM or graphics stack on a host. * `Host only` A variant with [minimal host](./adr/minimal-host.md) *only*. A user can manually install software to a host, including VMs (if supported by hardware). @@ -22,9 +22,9 @@ The main scope of the Ghaf platform is edge virtualization. However, to support | Variant Name | Headless | Graphics | VMs | Devices | |--- |--- |--- | --- | --- | -| `Default` | No | GUI VM [^note] | Supported | Jetson, generic x86 | +| `Default` | No | GUI VM | Supported | Jetson, generic x86 | | `Headless` | Yes | No | Supported | Jetson, generic x86 | | `Host Only` | Yes | No | May be supported but not included | Jetson, generic x86 | | `No Virtualization`| Yes or no | Native on host | Not supported | Raspberry Pi, RISC-V | -[^note] As of early 2023, the graphics stack is deployed on a host to support application development. Work is ongoing to define the GUI VM and isolate graphics with GPU passthrough. +[^note1]: As of early 2023, the graphics stack is deployed on a host to support application development. Work is ongoing to define the GUI VM and isolate graphics with GPU passthrough. diff --git a/docs/src/features/features.md b/docs/src/features/features.md index a22924b73..1a01b645c 100644 --- a/docs/src/features/features.md +++ b/docs/src/features/features.md @@ -26,7 +26,7 @@ Ghaf demo desktop and applications are illustrated in the screen capture below: - `aarch64`—generic AArch64; tested on an ARM server, laptop (e.g. Apple MacBook's), or NVIDIA Jetson AGX Orin. - `All variants`—supported devices from [Architectural Variants](https://tiiuae.github.io/ghaf/architecture/variants.html). -The following tables show the status of Ghaf Platform features: +The following tables show the status of the Ghaf Platform features: ## Release Builds and Hardware Architecture Support @@ -35,7 +35,7 @@ The following tables show the status of Ghaf Platform features: |-------------------|-------------|------------------|-------------------------------------| | Ghaf in virtual machine | ✅ | `x86` | `nix run .#packages.x86_64-linux.vm-debug` | | `aarch64` reference image | ✅ | `Orin` | Based on [Jetson Linux](https://developer.nvidia.com/embedded/jetson-linux), [OE4T](https://github.com/OE4T) and [jetpack-nixos](https://github.com/anduril/jetpack-nixos). | -| `aarch64` reference image | ✅ | `imx8qm` | Based on NXP BSP, implemented as [nixos-hardware module](https://github.com/NixOS/nixos-hardware/tree/master/nxp)| +| `aarch64` reference image | ✅ | `imx8mp` | Based on NXP BSP, implemented as [nixos-hardware module](https://github.com/NixOS/nixos-hardware/tree/master/nxp)| | `x86` generic image | ✅ | `x86` | Generic x86 computer, based on generic [NixOS](https://nixos.org/). NOTE: requires device specific configuration.| | `Lenovo X1` reference image | ✅ | `Lenovo X1` | x86_64 laptop computer, supports basic compartmentalized environment | | Native build | ✅ | `aarch64, x86` | Remote `aarc64` nixos builders recommended | @@ -50,28 +50,28 @@ The following tables show the status of Ghaf Platform features: |-------------------|-------------|------------------|----------------------------------------------| | Quick target update | ✅ | `all` | `nixos-rebuild --flake .#nvidia-jetson-orin-debug --target-host root@ghaf-host --fast switch` | | `aarch64` device flashing | ✅ | `Orin` | [Full device software flashing using `x86` machine](https://tiiuae.github.io/ghaf/ref_impl/build_and_run.html#flashing-nvidia-jetson-orin-agx) | -| root filesystem flashing | ✅ | `x86, imx8qm` | `dd` image to bootable media - [see](https://tiiuae.github.io/ghaf/ref_impl/build_and_run.html#running-ghaf-image-for-x86-computer) | +| root filesystem flashing | ✅ | `x86, imx8mp` | `dd` image to bootable media - [see](https://tiiuae.github.io/ghaf/ref_impl/build_and_run.html#running-ghaf-image-for-x86-computer) | | Debug: SSH | ✅ | `Orin`, `x86` | Host access only in `-debug`-target, see [authentication.nix](https://github.com/tiiuae/ghaf/blob/main/modules/development/authentication.nix) | | Debug: Serial | ✅ | `all` | Host access only in `-debug`-target - e.g. `screen /dev/ttyACM0 115200` | -| Compartmentalized environment | 🚧 | `Lenovo X1` | NetVM, GUI VM (with GPU passthrough) plus some Application VMs | +| Compartmentalized environment | 🚧 | `Lenovo X1` | Net VM, GUI VM (with GPU passthrough) plus some App VMs | ## Target Architecture | Feature | Status | Reference Device | Details | |-------------------|-------------|------------------|----------------------------------------------| -| `minimal host` | 🚧 | [`all`](https://tiiuae.github.io/ghaf/architecture/variants.html) | See [Minimal Host](https://tiiuae.github.io/ghaf/architecture/adr/minimal-host.html) and [PR #140](https://github.com/tiiuae/ghaf/pull/140). | -| `netvm` | ✅ | `Orin` | See [netvm](https://tiiuae.github.io/ghaf/architecture/adr/netvm.html). Passthrough with Wifi works but requires SSID/password configuration | -| `idsvm` | ✅ | `Orin` | [Defensive security VM placeholder PR open](https://github.com/tiiuae/ghaf/pull/146) | -| `guivm` | 🚧 | `All`, `Lenovo X1`| Implemented for Lenovo X1 reference device, other devices have Wayland compositor running on the host.| -| `appvm` | 🚧 | `All`, `Lenovo X1`| Implemented for Lenovo X1 reference device: chromium, GALA and zathura VMs. Requires `guivm` in place | -| `adminvm` | ✅ | `All` | Not started | -| Inter VM comms - IP-based | 🚧 | `All` |`-debug`-targets have network bridges to access VMs from host | +| Minimal host | 🚧 | [`all`](https://tiiuae.github.io/ghaf/architecture/variants.html) | See [Minimal Host](https://tiiuae.github.io/ghaf/architecture/adr/minimal-host.html) and [PR #140](https://github.com/tiiuae/ghaf/pull/140). | +| Net VM | ✅ | `Orin` | See [Net VM](https://tiiuae.github.io/ghaf/architecture/adr/netvm.html). Passthrough with Wi-Fi works but requires SSID/password configuration. | +| IDS VM | ✅ | `Orin`, `Lenovo X1` | [Defensive networking mechanism](/docs/src/architecture/adr/idsvm.md). | +| GUI VM | 🚧 | `All`, `Lenovo X1`| Implemented for Lenovo X1 reference device, other devices have Wayland compositor running on the host.| +| App VM | 🚧 | `All`, `Lenovo X1`| Implemented for Lenovo X1 reference device: Chromium, GALA and Zathura VMs. Requires GUI VM in place. | +| Admin VM | ✅ | `All` | Not started | +| Inter VM comms - IP-based | 🚧 | `All` |`-debug`-targets have network bridges to access VMs from host. | | Inter VM comms - shared memory | 🚧 | `All` | | -| Inter VM Wayland | 🚧 | `All` | Currently it is `waypipe` over SSH, for test and demo purpose only | -| SW update | 🚧 | `All` | A/B update tooling being evaluated | -| USB passthrough | 🚧 | `Orin` | No reference implementation integrated yet | -| PCI passthrough | ✅ | `All` | Used for reference in `netvm` on `Orin` | +| Inter VM Wayland | 🚧 | `All` | Currently it is `waypipe` over SSH, for test and demo purpose only. | +| SW update | 🚧 | `All` | A/B update tooling being evaluated. | +| USB passthrough | 🚧 | `Orin` | No reference implementation integrated yet. | +| PCI passthrough | ✅ | `All` | Used for reference in Net VM on `Orin`. | | UART passthrough | 🚧 | `Orin` | See [NVIDIA Jetson AGX Orin: UART Passthrough](https://tiiuae.github.io/ghaf/build_config/passthrough/nvidia_agx_pt_uart.html). Not integrated to any VM. | | ARM platform bus devices passthrough | 🚧 | `Orin` | NVIDIA BPMP virtualization being developed | diff --git a/docs/src/img/idsvm.drawio.png b/docs/src/img/idsvm.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..0f890d297fa3ca64f19612b167615d0548ca55d8 GIT binary patch literal 35276 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 literal 0 HcmV?d00001 diff --git a/docs/src/ref_impl/build_and_run.md b/docs/src/ref_impl/build_and_run.md index 0a8cca43a..e5acdc2ed 100644 --- a/docs/src/ref_impl/build_and_run.md +++ b/docs/src/ref_impl/build_and_run.md @@ -9,7 +9,8 @@ This tutorial assumes that you already have basic [git](https://git-scm.com/) ex The canonical URL for the upstream Ghaf git repository is . To try Ghaf, you can build it from the source. ->[Cross-compilation](../ref_impl/cross_compilation.md) support is currently under development and not available for the building process. +> [!WARNING] +> [Cross-compilation](../ref_impl/cross_compilation.md) support is currently under development and not available for the building process. ## Prerequisites @@ -32,10 +33,9 @@ Then you can use one of the following instructions for the supported targets: | Generic x86 Сomputer | x86_64 | [Running Ghaf Image for x86 Computer](./build_and_run.md#running-ghaf-image-for-x86-computer) | | Lenovo X1 Carbon Gen 11 | x86_64 | [Running Ghaf Image for Lenovo X1](./build_and_run.md#running-ghaf-image-for-lenovo-x1) | | NVIDIA Jetson AGX Orin | AArch64 | [Ghaf Image for NVIDIA Jetson Orin AGX](./build_and_run.md#ghaf-image-for-nvidia-jetson-orin-agx) | -| NXP i.MX 8QM-MEK | AArch64 | [Building Ghaf Image for NXP i.MX 8QM-MEK](./build_and_run.md#building-ghaf-image-for-nxp-imx-8qm-mek) | +| NXP i.MX 8MP-EVK | AArch64 | [Building Ghaf Image for NXP i.MX 8MP-EVK](./build_and_run.md#building-ghaf-image-for-nxp-imx-8mp-evk) | | MICROCHIP icicle-kit | RISCV64 | [Building Ghaf Image for Microchip Icicle Kit](./build_and_run.md#building-ghaf-image-for-microchip-icicle-kit) | - --- ## Running Ghaf Image for x86 VM (ghaf-host) @@ -57,9 +57,9 @@ Do the following: ``` nix build github:tiiuae/ghaf#generic-x86_64-debug ``` -2. After the build is completed, prepare a USB boot media with the target image you built: +2. After the build is completed, prepare a USB boot media with the target image you built using the `flash.sh` script: ``` - dd if=./result/nixos.img of=/dev/ bs=32M status=progress oflag=direct + ./packages/flash/flash.sh -d /dev/ -i result/ ``` 3. Boot the computer from the USB media. @@ -74,9 +74,9 @@ Do the following: ``` nix build github:tiiuae/ghaf#lenovo-x1-carbon-gen11-debug ``` -2. After the build is completed, prepare a USB boot media with the target image you built: +2. After the build is completed, prepare a USB boot media with the target image you built using the `flash.sh` script: ``` - dd if=./result/nixos.img of=/dev/ bs=32M status=progress oflag=direct + ./packages/flash/flash.sh -d /dev/ -i result/ ``` 3. Boot the computer from the USB media. @@ -103,6 +103,7 @@ Before you begin: 2. Connect a Linux laptop to the board with the USB-C cable. 3. Connect the Linux laptop to the board with a Micro-USB cable to use [serial interface](https://developer.ridgerun.com/wiki/index.php/NVIDIA_Jetson_Orin/In_Board/Getting_in_Board/Serial_Console). + > [!NOTE] > For more information on the board's connections details, see the [Hardware Layout](https://developer.nvidia.com/embedded/learn/jetson-agx-orin-devkit-user-guide/developer_kit_layout.html) section of the Jetson AGX Orin Developer Kit User Guide. 3. After the build is completed, put the board in recovery mode. For more information, see the [Force Recovery](https://developer.nvidia.com/embedded/learn/jetson-agx-orin-devkit-user-guide/howto.html#force-recovery-mode) Mode section in the Jetson AGX Orin Developer Kit User Guide. @@ -127,9 +128,9 @@ After the latest firmware is [flashed](./build_and_run.md#flashing-nvidia-jetson ``` nix build github:tiiuae/ghaf#nvidia-jetson-orin-agx-debug ``` -2. After the build is completed, prepare a USB boot media with the target image you built: +2. After the build is completed, prepare a USB boot media with the target image you built using the `flash.sh` script: ``` - dd if=./result/nixos.img of=/dev/ bs=32M status=progress oflag=direct + ./packages/flash/flash.sh -d /dev/ -i result/sd-image/ ``` 3. Boot the hardware from the USB media. @@ -159,25 +160,17 @@ In the current state of Ghaf, it is a bit tricky to make NVIDIA Jetson Orin AGX --- -## Building Ghaf Image for NXP i.MX 8QM-MEK +## Building Ghaf Image for NXP i.MX 8MP-EVK Before you begin, check device-independent [prerequisites](./build_and_run.md#prerequisites). -In the case of i.MX8, Ghaf deployment consists of creating a bootable SD card with a first-stage bootloader (Tow-Boot) and USB media with the Ghaf image: - -1. To build and flash [**Tow-Boot**](https://github.com/tiiuae/Tow-Boot) bootloader: - - ``` - $ git clone https://github.com/tiiuae/Tow-Boot.git && cd Tow-Boot - $ nix-build -A imx8qm-mek - $ sudo dd if=result/ shared.disk-image.img of=/dev/ - ``` +In the case of i.MX8, Ghaf deployment consists of creating a bootable SD card and USB media with the Ghaf image: -2. To build and flash the Ghaf image: - 1. Run the `nix build .#packages.aarch64-linux.imx8qm-mek-release` command. - 2. Prepare the USB boot media with the target HW image you built: `dd if=./result/nixos.img of=/dev/ bs=32M status=progress oflag=direct`. +1. To build and flash the Ghaf image: + 1. Run the `nix build .#packages.aarch64-linux.imx8mp-evk-release` command. + 2. Prepare the USB boot media with the target HW image you built: `./packages/flash/flash.sh -d /dev/ -i result/`. -3. Insert an SD card and USB boot media into the board and switch the power on. +2. Insert an SD card and USB boot media into the board and switch the power on. --- @@ -199,11 +192,11 @@ In the case of the Icicle Kit, Ghaf deployment consists of creating an SD image 2. Flash the Ghaf SD image: * If you want to use a SD card: - * Prepare the SD card with the target HW image you built: `dd if=./result/nixos.img of=/dev/ bs=32M status=progress oflag=direct`. + * Prepare the SD card with the target HW image you built: `./packages/flash/flash.sh -d /dev/ -i result/`. * Insert an SD card into the board and switch the power on. * If you want to use the onboard MMC: - * You can directly flash a NixOS image to onboard an MMC card: `dd if=./result/nixos.img of=/dev/ bs=32M status=progress oflag=direct`. + * You can directly flash a NixOS image to an onboard MMC card: `./packages/flash/flash.sh -d /dev/ -i result/`. For more information on how to access the MMC card as a USB disk, see [MPFS Icicle Kit User Guide](https://tinyurl.com/48wycdka). diff --git a/docs/src/ref_impl/creating_appvm.md b/docs/src/ref_impl/creating_appvm.md index e394a7fea..9517408d2 100644 --- a/docs/src/ref_impl/creating_appvm.md +++ b/docs/src/ref_impl/creating_appvm.md @@ -5,69 +5,60 @@ # Creating Application VM -Application VM (AppVM) is a VM that improves trust in system components by isolating applications from the host OS and other applications. Virtualization with hardware-backed mechanisms provides better resource protection than traditional OS. This lets users use applications of different trust levels within the same system without compromising system security. While the VMs have overhead, it is acceptable as a result of improved security and usability that makes the application seem like it is running inside an ordinary OS. +Application VM (App VM) is a VM that improves trust in system components by isolating applications from the host OS and other applications. Virtualization with hardware-backed mechanisms provides better resource protection than traditional OS. This lets users use applications of different trust levels within the same system without compromising system security. While the VMs have overhead, it is acceptable as a result of improved security and usability that makes the application seem like it is running inside an ordinary OS. -As a result, both highly trusted applications and untrusted applications can be hosted in the same secure system when the concerns are separated in their own AppVMs. +As a result, both highly trusted applications and untrusted applications can be hosted in the same secure system when the concerns are separated in their own App VM. -To create an AppVM: -1. Add AppVM description. -2. Add an app launcher in GUI VM. +To create an App VM, do the following: +1. Create the new configuration file for your VM in the [modules/reference/appvms](https://github.com/tiiuae/ghaf/tree/main/modules/reference/appvms) directory. + You can use an already existing VM file as a reference, for example: `modules/reference/appvms/business.nix`. -## Adding AppVM Description + Each VM has the following properties: -Add the VM description in the target configuration. + | **Property** | **Type** | **Unique** | **Description** | **Example** | + | -------------- | --------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------- | --------------------- | + | name | str | yes | This name is postfixed with `-vm` and will be shown in microvm list. The name, for example, `chromium-vm` will be also the VM hostname. The length of the name must be 8 characters or less. | “chromium” | + | packages | list of types.package | no | Packages to include in a VM. It is possible to make it empty or add several packages. | [chromium top] | + | macAddress | str | yes | Needed for network configuration. | "02:00:00:03:03:05" | + | ramMb | int, [1, …, host memory] | no | Memory in MB. | 3072 | + | cores | int, [1, …, host cores] | no | Virtual CPU cores. -[lenovo-x1-carbon.nix](https://github.com/tiiuae/ghaf/blob/main/targets/lenovo-x1-carbon.nix) already has AppVMs inside for Chromium, Gala, and Zathura applications. - - -#### AppVMs Example +2. Create a new option for your VM in [modules/reference/appvms/default.nix](https://github.com/tiiuae/ghaf/blob/main/modules/reference/appvms/default.nix). For example: ``` -vms = with pkgs; [ - { - name = "chromium"; - packages = [chromium]; - macAddress = "02:00:00:03:03:05"; - ramMb = 3072; - cores = 4; - } - { - name = "gala"; - packages = [(pkgs.callPackage ../packages/gala {})]; - macAddress = "02:00:00:03:03:06"; - ramMb = 1536; - cores = 2; - } - { - name = "zathura"; - packages = [zathura]; - macAddress = "02:00:00:03:03:07"; - ramMb = 512; - cores = 1; - } -]; + business-vm = lib.mkEnableOption "Enable the Business appvm"; + new-vm = lib.mkEnableOption "Enable the New appvm"; # your new vm here ``` -Each VM has the following properties: - - -| **Property** | **Type** | **Unique** | **Description** | **Example** | -| -------------- | --------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------- | --------------------- | -| name | str | yes | This name is postfixed with `-vm` and will be shown in microvm list. The name - e.g. `chromium-vm` will be also the VM hostname. The lenght of the name must be 8 characters or less. | “chromium” | -| packages | list of types.package | no | Packages to include in a VM. It is possible to make it empty or add several packages. | [chromium top] | -| macAddress | str | yes | Needed for network configuration. | "02:00:00:03:03:05" | -| ramMb | int, [1, …, host memory] | no | Memory in MB. | 3072 | -| cores | int, [1, …, host cores] | no | Virtual CPU cores. | 4 | +``` + ++ (lib.optionals cfg.business-vm [(import ./business.nix {inherit pkgs lib config;})]) + ++ (lib.optionals cfg.new-vm [(import ./new_vm_name.nix {inherit pkgs lib config;})]); # your new vm here +``` +3. Add your new VM to the profile file, for example [mvp-user-trial.nix](https://github.com/tiiuae/ghaf/blob/main/modules/profiles/mvp-user-trial.nix): -## Adding Application Launcher in GUI VM +``` + business-vm = true; + new-vm = true; # your new vm here +``` -To add an application launcher, add an element in the [guivm.nix](https://github.com/tiiuae/ghaf/blob/main/modules/virtualization/microvm/guivm.nix) file to the **graphics.weston.launchers** list. +> [!NOTE] +> For more information on creating new profiles, see [Profiles Configuration](./profiles-config.md). -A launcher element has two properties: +4. Add an IP and the VM name in [modules/common/networking/hosts.nix](https://github.com/tiiuae/ghaf/blob/main/modules/common/networking/hosts.nix). For example: + +``` + { + ip = 105; + name = "business-vm"; + } +``` -* **path**–path to the executable you want to run, like a graphical application; -* **icon**–path to an icon to show. +5. Add an application launcher in [modules/common/services/desktop.nix](https://github.com/tiiuae/ghaf/blob/main/modules/common/services/desktop.nix). + + A launcher element has the following properties: -Check the example launchers at [guivm.nix](https://github.com/tiiuae/ghaf/blob/main/modules/virtualization/microvm/guivm.nix). + * **name**: the name of the launcher; + * **path**: path to the executable you want to run, like a graphical application; + * **icon**: path to an icon to show. If you have an icon package for your launcher, add it here as well: [packages/icon-pack/default.nix](https://github.com/tiiuae/ghaf/blob/main/packages/icon-pack/default.nix). \ No newline at end of file diff --git a/docs/src/ref_impl/cross_compilation.md b/docs/src/ref_impl/cross_compilation.md index 416a3b511..404dca0e1 100644 --- a/docs/src/ref_impl/cross_compilation.md +++ b/docs/src/ref_impl/cross_compilation.md @@ -5,6 +5,7 @@ # Cross-Compilation +> [!WARNING] > Cross-compilation is currently under development and cannot be used properly on all the supported device configurations. Ghaf is targeted at a range of devices and form factors that support different instruction set architectures (ISA). Many small form-factor edge devices are not powerful enough to compile the needed applications or OSs that run on them. As the most common ISA used in desktops and servers is ``x_86``, this will generally require that the code is cross-compiled for target ISA e.g. ``AArch64`` or ``RISC-V``. diff --git a/docs/src/ref_impl/development.md b/docs/src/ref_impl/development.md index 88845c3c7..d46820587 100644 --- a/docs/src/ref_impl/development.md +++ b/docs/src/ref_impl/development.md @@ -14,17 +14,22 @@ The scope of target support is updated with development progress: * [Installer](./installer.md) * [Cross-Compilation](./cross_compilation.md) * [Creating Application VM](./creating_appvm.md) +* [Hardware Configuration](ref_impl/hw-config.md) +* [Profiles Configuration](ref_impl/profiles-config.md) * [labwc Desktop Environment](./labwc.md) +* [IDS VM Further Development](./idsvm-development.md) +* [systemd Service Hardening](./systemd-service-config.md) -Once you are up and running, you can participate in the collaborative development process by building a development build with additional options. For example, with the development username and password that are defined in [accounts.nix](https://github.com/tiiuae/ghaf/blob/main/modules/users/accounts.nix). +Once you are up and running, you can participate in the collaborative development process by building a development build with additional options. For example, with the development username and password that are defined in [accounts.nix](https://github.com/tiiuae/ghaf/blob/main/modules/common/users/accounts.nix). -If you authorize your development SSH keys in the [ssh.nix](https://github.com/tiiuae/ghaf/blob/main/modules/development/ssh.nix#L10-L23) module and rebuild Ghaf for your target device, you can use `nixos-rebuild switch` to quickly deploy your configuration changes to the target device over the network using SSH. For example: +If you authorize your development SSH keys in the [ssh.nix](https://github.com/tiiuae/ghaf/blob/main/modules/common/development/authorized_ssh_keys.nix#L4-L21) module and rebuild Ghaf for your target device, you can use `nixos-rebuild switch` to quickly deploy your configuration changes to the target device over the network using SSH. For example: nixos-rebuild --flake .#nvidia-jetson-orin-agx-debug --target-host root@ --fast switch ... nixos-rebuild --flake .#lenovo-x1-carbon-gen11-debug --target-host root@ --fast switch ... +> [!TIP] > With the `-debug` targets, the debug ethernet is enabled on host. With Lenovo X1 Carbon, you can connect USB-Ethernet adapter for the debug and development access. Pull requests are the way for contributors to submit code to the Ghaf project. For more information, see [Contribution Guidelines](../appendices/contributing_general.md). diff --git a/docs/src/ref_impl/example_project.md b/docs/src/ref_impl/example_project.md index ab90a47e6..85e745f1c 100644 --- a/docs/src/ref_impl/example_project.md +++ b/docs/src/ref_impl/example_project.md @@ -11,7 +11,7 @@ The best way to do the Ghaf customization is by using Ghaf templates: 1. Create a template project as described in the [Ghaf as Library](../ref_impl/ghaf-based-project.md) section. 2. Adjust your system configuration in accordance with your HW specification. Determine all VIDs and PIDs of the devices that are passed to the VMs. -3. Add GUIVM configuration, NetworkVM configuration, and optionally some AppVMs. +3. Add GUI VM configuration, NetworkVM configuration, and optionally some AppVMs. 4. Set up Weston panel shortcuts. You can refer to the existing [project example for Lenovo T14 and Lenovo X1 laptops](https://github.com/unbel13ver/ghaf-lib). @@ -49,13 +49,14 @@ If after booting you see a black screen, try the following to detect the issue: 3. Identify an IP address by a MAC address with the `arp` command. If a MAC address is unknown, you can boot into the NixOS image or any other OS to find it, or try the latest addresses that `arp` returns. 4. Connect using SSH (login/password ghaf/ghaf). Then connect from netvm to the host using `ssh 192.168.101.2` (login/password ghaf/ghaf). 5. Check running VMs with `microvm -l`. -6. Check a GUIVM log using `journalctl -u microvm@guivm`. -7. If GUIVM does not start, you can try to start it manually with `/var/lib/microvms/guivm/current/bin/microvm-run`. +6. Check a GUI VM log using `journalctl -u microvm@guivm`. +7. If GUI VM does not start, you can try to start it manually with `/var/lib/microvms/guivm/current/bin/microvm-run`. -In case when GUIVM did not start with the error message that the device /dev/mouse or /dev/touchpad was not found, it means that the model of the touchpad in the laptop is different since it was bought in another country and has a different SKU (stock keeping unit). To add support for a new touchpad, do the following: +In case when GUI VM did not start with the error message that the device /dev/mouse or /dev/touchpad was not found, it means that the model of the touchpad in the laptop is different since it was bought in another country and has a different SKU (stock keeping unit). To add support for a new touchpad, do the following: 1. On the ghaf host, check the devices in `/dev/input/by-path` that contain “-event-” in the name. Use the command like `udevadm info -q all -a /dev/input/by-path/pci-0000:00:15.0-platform-i2c_designware.0-event-mouse | grep name` for the name of each of these devices. + > [!TIP] > By name you can understand which devices belong to the touchpad. For example, on laptops in Finland they look like “SYNA8016:00 06CB:CEB3 Mouse” and “SYNA8016:00 06CB:CEB3 Touchpad”, and in the UAE they are “ELAN067C:00 04F3:31F9 Mouse” and “ELAN067C:00 04F3:31F9 Touchpad.” 2. If there are no such devices in `/dev/input/by-path`, then you can check the devices /dev/input/event* with a similar command. diff --git a/docs/src/ref_impl/hw-config.md b/docs/src/ref_impl/hw-config.md new file mode 100644 index 000000000..d62f00af0 --- /dev/null +++ b/docs/src/ref_impl/hw-config.md @@ -0,0 +1,26 @@ + + +# Hardware Configuration + +All configuration files for reference target devices are in [modules/hardware](https://github.com/tiiuae/ghaf/tree/main/modules/hardware). + +The ghaf-24.06 release supports the following target hardware: + +* NVIDIA Jetson AGX Orin +* NVIDIA Jetson Orin NX +* Generic x86 (PC) +* Polarfire Icicle Kit +* Lenovo ThinkPad X1 Carbon Gen 11 +* Lenovo ThinkPad X1 Carbon Gen 10 +* NXP i.MX 8M Plus + +To add a new hardware configuration file, do the following: + +1. Create a separate folder for the device in [modules/hardware](https://github.com/tiiuae/ghaf/tree/main/modules/hardware). +2. Create the new configuration file with hardware-dependent parameters like host information, input and output device parameters, and others. + + > [!TIP] + > You can use an already existing file as a reference, for example [modules/hardware/lenovo-x1/definitions/x1-gen11.nix](https://github.com/tiiuae/ghaf/blob/main/modules/hardware/lenovo-x1/definitions/x1-gen11.nix). diff --git a/docs/src/ref_impl/idsvm-development.md b/docs/src/ref_impl/idsvm-development.md new file mode 100644 index 000000000..605df602e --- /dev/null +++ b/docs/src/ref_impl/idsvm-development.md @@ -0,0 +1,36 @@ + + +# IDS VM Further Development + + +## Implementation + +The [IDS VM](../architecture/adr/idsvm.md) is implemented as a regular Micro VM with static IP. + +The [mitmproxy](https://mitmproxy.org/) is included in the demonstrative interactive proxy to enable analysis of TLS-protected data on the fly. Also, [Snort](https://snort.org/) network intrusion detection and prevention system package is included but no dedicated UI nor proper utilization is provided. + +Enforcing network traffic to go through IDS VM is crucial to the IDS VM functionality. It is achieved by setting the IDS VM to be the gateway of other VMs in [dnsmasq](https://thekelleys.org.uk/dnsmasq/doc.html) configuration of Net VM. There is a risk that one could change the gateway settings of the VM to bypass the IDS VM. This however requires root (sudo) rights and it is assumed here that these rights are enabled only in the debug build. + + +## mitmproxy + +[**mitmproxy**](https://mitmproxy.org/) is a free and open-source interactive HTTPS proxy. It is your Swiss Army Knife for debugging, testing, privacy measurements, and penetration testing. It can be used to intercept, inspect, modify and replay web traffic such as HTTP/1, HTTP/2, WebSockets, or any other SSL/TLS-protected protocols. + +In IDS VM, we use **mitmweb**[^note1] tool to demonstrate mitmproxy's capabilities. It provides a web-based user interface that allows interactive examination and modification of HTTP(s) traffic. The mtmproxy package also includes a console tool that provides the same functionalities in a text-based interface and a command-line tool **mitmdump** to view, record, and programmatically transform HTTP(s) traffic. + +The mitmweb tool is run in *ids-vm* as a systemd service. It starts automatically when *ids-vm* boots up. The UI it provides is accessible at , so it is available from *ids-vm* only. However, with SSH port forwarding it is possible to access the UI from other VMs. To that purpose, GUI VM has a script *mitmweb-ui* that creates an SSH tunnel between *ids-vm* and *chromium-vm*, launches Chromium, and connects to the UI address. + + +## Certificates + +mitmproxy can decrypt encrypted traffic on the fly, as long as the client trusts mitmproxy's built-in certificate authority (CA). CA certificates are the same for all *ids-vm* instances, as they are hardcoded to the IDS VM implementation. In the release version, these should be randomly generated and stored securely. + +By default, any of the clients should not trust mitmproxy's CA. These CA certificates should be installed in the OS's CA storage. However, many client applications (web browsers, for example) use their own CA bundles, and importing custom certificates there can be complicated or require manual user interaction. In our case, this difficulty is circumvented in *chromium-vm* by disabling certificate verification errors, if the certificate chain contains a certificate which SPKI fingerprint matches that of mitmproxy's CA certificate fingerprint. This does not degrade server verification security since mitmproxy validates upstream certificates using a certified Python package which provides Mozilla's CA Bundle. + +Some applications use certificate pinning to prevent man-in-the-middle attacks. As a consequence mitmproxy's certificates will not be accepted by these applications without patching applications manually. Other option is to set mitmproxy to use ignore_hosts option to prevent mitmproxy from intercepting traffic to these specific domains. + + +[^note1]: **mitmproxy** is an interactive, SSL/TLS-capable intercepting proxy with a console interface for HTTP/1, HTTP/2, and WebSockets. **mitmweb** is a web-based interface for mitmproxy. **mitmdump** is the command-line version of mitmproxy. Source: [mitmproxy docs](https://docs.mitmproxy.org/stable/#3-powerful-core-tools). diff --git a/docs/src/ref_impl/installer.md b/docs/src/ref_impl/installer.md index 1ddb233b6..e7e4f4a5b 100644 --- a/docs/src/ref_impl/installer.md +++ b/docs/src/ref_impl/installer.md @@ -5,22 +5,23 @@ # Installer + ## Configuring and Building Installer for Ghaf You can obtain the installation image for your Ghaf configuration. -In addition to the live USB image that Ghaf provides it is also possible -to install Ghaf. This can either be achieved by downloading the desired image -or by building it as described below. +In addition to the live USB image that Ghaf provides it is also possible to install Ghaf. This can either be achieved by downloading the desired image or by building it as described below. + +Currently, only x86_64-linux systems are supported by the standalone installer. -Currently only x86_64-linux systems are supported by the standalone installer. So to build e.g. the debug image -for the Lenovo x1 follow the following steps +To build, for example, the debug image for the Lenovo x1, use the following command: ```sh nix build .#lenovo-x1-carbon-gen11-debug-installer ``` -## Flashing the installer + +## Flashing Installer Once built you must transfer it to the desired installation media. It requires at least a 4GB SSD, at the time of writing. @@ -28,24 +29,25 @@ Once built you must transfer it to the desired installation media. It requires a sudo dd if=./result/iso/ghaf--x86_64-linux.iso of=/dev/ bs=32M status=progress; sync ``` -## Installing the image -**Warning this is a destructive operation and will overwrite your system** +## Installing Image + +> [!CAUTION] +> This operation is destructive and will overwrite your system. Insert the SSD into the laptop, boot, and select the option to install. -When presented with the terminal run: +Then use the following command: ```nix sudo ghaf-install.sh ``` -Check the available options shown in the prompt for the install target -remember that the `/dev/sdX` is likely the install medium. +Check the available options shown in the prompt for the install target. Mind that the `/dev/sdX` is likely the install medium. -Once entered, remembering to include `/dev`, press ENTER to complete the process. +Once entered, include `/dev` and press [Enter] on the keyboard to complete the process. ```nix sudo reboot ``` -And remember to remove the installer drive +Remove the installer drive. diff --git a/docs/src/ref_impl/labwc.md b/docs/src/ref_impl/labwc.md index 117c0001c..398d8c43f 100644 --- a/docs/src/ref_impl/labwc.md +++ b/docs/src/ref_impl/labwc.md @@ -12,7 +12,7 @@ To use labwc as your default desktop environment, add it as a module to Ghaf: * change the configuration option `profiles.graphics.compositor = "labwc"` or -* uncomment the corresponding line in the [guivm.nix](https://github.com/tiiuae/ghaf/blob/main/modules/virtualization/microvm/guivm.nix) file. +* uncomment the corresponding line in the [guivm.nix](https://github.com/tiiuae/ghaf/blob/main/modules/microvm/virtualization/microvm/guivm.nix) file. The basis of the labwc configuration is the set of following files: `rc.xml`, `menu.xml`, `autostart`, and `environment`. These files can be edited by substituting in the labwc overlay `overlays/custom-packages/labwc/default.nix`. @@ -24,7 +24,8 @@ The border color concept illustrates the application trustworthiness in a user-f Ghaf uses patched labwc which makes it possible to change the border color for the chosen application. The implementation is based on window rules by substituting the server decoration colors (`serverDecoration` = `yes`). The `borderColor` property is responsible for the frame color. -> **TIP:** According to the labwc specification, the **identifier** parameter is case-sensitive and relates to app_id for native Wayland windows and WM_CLASS for XWayland clients. +> [!IMPORTANT] +> According to the labwc specification, the **identifier** parameter is case-sensitive and relates to app_id for native Wayland windows and WM_CLASS for XWayland clients. For example, the foot terminal with Aqua colored frame: ``` diff --git a/docs/src/ref_impl/profiles-config.md b/docs/src/ref_impl/profiles-config.md new file mode 100644 index 000000000..30f9f71b3 --- /dev/null +++ b/docs/src/ref_impl/profiles-config.md @@ -0,0 +1,48 @@ + + +# Profiles Configuration + +A profile is a set of software needed for a particular use case. All profiles configuration files are in [modules/profiles](https://github.com/tiiuae/ghaf/tree/main/modules/profiles). + +To add a new profile, do the following: + +1. Create your own configuration file using [modules/profiles/mvp-user-trial.nix](https://github.com/tiiuae/ghaf/blob/main/modules/profiles/mvp-user-trial.nix) as a reference. +2. Depending on the location of your reference appvms, services, or programs change the includes to point to them. +3. Create a new enable option to enable the profile, for example, `new-cool-profile`. +4. In the lower section, under the correct area appvms, services, programs, make sure to describe additional definitions you need. + + +For example, a `safe-and-unsave-browsing.nix` file with a simple setup that includes business-vm and chrome-vm could look like this: + +``` + config = lib.mkIf cfg.enable { + ghaf = { + reference = { + appvms = { + enable = true; + chromium-vm = true; + business-vm = true; + }; + + services = { + enable = true; + }; + + programs = { + }; + }; + + profiles = { + laptop-x86 = { + enable = true; + netvmExtraModules = [../reference/services]; + guivmExtraModules = [../reference/programs]; + inherit (config.ghaf.reference.appvms) enabled-app-vms; + }; + }; + }; + }; +``` \ No newline at end of file diff --git a/docs/src/ref_impl/reference_implementations.md b/docs/src/ref_impl/reference_implementations.md index e90d40adc..83c713c79 100644 --- a/docs/src/ref_impl/reference_implementations.md +++ b/docs/src/ref_impl/reference_implementations.md @@ -26,6 +26,7 @@ NixOS, a Linux OS distribution packaged with Nix, provides us with: Even when unmodified upstream is often preferred, even ideal, to ensure timely security updates from upstream—customizations are sometimes required. + ### Example To support a reference board without a vendor board support package (BSP)—bootloader, kernel, device drivers—is often not feasible. With this approach, we can overlay the generic NixOS Linux kernel with the vendor kernel and add a vendor bootloader to build a target image. @@ -39,9 +40,15 @@ The same goes with the architectural variants as headless devices or end-user de - [Development](./development.md) - [Build and Run](./build_and_run.md) + - [Running Remote Build on NixOS](./remote_build_setup.md) - [Installer](./installer.md) - [Cross-Compilation](./cross_compilation.md) - [Creating Application VM](./creating_appvm.md) + - [Hardware Configuration](ref_impl/hw-config.md) + - [Profiles Configuration](ref_impl/profiles-config.md) + - [labwc Desktop Environment](./labwc.md) + - [IDS VM Further Development](./idsvm-development.md) + - [systemd Service Hardening](./systemd-service-config.md) - [Ghaf as Library: Templates](./ghaf-based-project.md) - [Example Project](./example_project.md) - [Modules Options](./modules_options.md) diff --git a/docs/src/ref_impl/remote_build_setup.md b/docs/src/ref_impl/remote_build_setup.md index 0a7652710..42b98b919 100644 --- a/docs/src/ref_impl/remote_build_setup.md +++ b/docs/src/ref_impl/remote_build_setup.md @@ -15,6 +15,7 @@ If you hit an issue, check [Troubleshooting](./remote_build_setup.md#troubleshoo ### 1. Configuring SSH Keys +> [!IMPORTANT] > This step assumes that public SSH keys were generated and copied (*ssh-copy-id*) both for normal and root users. For more information, see [Setting up public key authentication](https://www.ssh.com/academy/ssh/copy-id#setting-up-public-key-authentication). Before you begin, make sure an SSH connection is established to the remote host for both normal and root users: @@ -57,7 +58,8 @@ Do the following on a local machine: ``` cd .ssh ``` - > **TIP**:`.ssh` is a user-level access and `/etc/ssh` is system-wide. + > [!TIP] + > `.ssh` is a user-level access and `/etc/ssh` is system-wide. #### 1.2. Accessing Remote Machine Using SSH diff --git a/docs/src/ref_impl/systemd-service-config.md b/docs/src/ref_impl/systemd-service-config.md new file mode 100644 index 000000000..c3cb12682 --- /dev/null +++ b/docs/src/ref_impl/systemd-service-config.md @@ -0,0 +1,614 @@ + + +# systemd Service Hardening + +This document outlines systemd service configurations that significantly impact a service's exposure. The following configurations can be utilized to enhance the security of a systemd service: + + + + + + + + +
+ +1. Networking + - [PrivateNetwork](./systemd-service-config.md#11-privatenetwork) + - [IPAccounting](./systemd-service-config.md#12-ipaccounting) + - [IPAddressAllow, IPAddressDeny](./systemd-service-config.md#13-ipaddressallow-ipaddressdeny) + - [RestrictNetworkInterfaces](./systemd-service-config.md#14-restrictnetworkinterfaces) + - [RestrictAddressFamilies](./systemd-service-config.md#15-restrictaddressfamilies) +2. File system + - [ProtectHome](./systemd-service-config.md#21-protecthome) + - [ProtectSystem](./systemd-service-config.md#22-protectsystem) + - [ProtectProc](./systemd-service-config.md#23-protectproc) + - [ReadWritePaths, ReadOnlyPaths, InaccessiblePaths, ExecPaths, NoExecPaths](./systemd-service-config.md#24-readwritepaths-readonlypaths-inaccessiblepaths-execpaths-noexecpaths) + - [PrivateTmp](./systemd-service-config.md#25-privatetmp) + - [PrivateMounts](./systemd-service-config.md#26-privatemounts) + - [ProcSubset](./systemd-service-config.md#27-procsubset) +3. User separation + - [PrivateUsers](./systemd-service-config.md#31-privateusers) + - [DynamicUser](./systemd-service-config.md#32-dynamicuser) +4. Devices + - [PrivateDevices](./systemd-service-config.md#41-privatedevices) + - [DeviceAllow](./systemd-service-config.md#42-deviceallow) +5. Kernel + - [ProtectKernelTunables](./systemd-service-config.md#51-protectkerneltunables) + - [ProtectKernelModules](./systemd-service-config.md#52-protectkernelmodules) + - [ProtectKernelLogs](./systemd-service-config.md#53-protectkernellogs) + + + +6. Misc + - [Delegate](./systemd-service-config.md#61-delegate) + - [KeyringMode](./systemd-service-config.md#62-keyringmode) + - [NoNewPrivileges](./systemd-service-config.md#63-nonewprivileges) + - [UMask](./systemd-service-config.md#64-umask) + - [ProtectHostname](./systemd-service-config.md#65-protecthostname) + - [ProtectClock](./systemd-service-config.md#66-protectclock) + - [ProtectControlGroups](./systemd-service-config.md#67-protectcontrolgroups) + - [RestrictNamespaces](./systemd-service-config.md#68-restrictnamespaces) + - [LockPersonality](./systemd-service-config.md#69-lockpersonality) + - [MemoryDenyWriteExecute](./systemd-service-config.md#610-memorydenywriteexecute) + - [RestrictRealtime](./systemd-service-config.md#611-restrictrealtime) + - [RestrictSUIDSGID](./systemd-service-config.md#612-restrictsuidsgid) + - [RemoveIPC](./systemd-service-config.md#613-removeipc) + - [SystemCallArchitectures](./systemd-service-config.md#614-systemcallarchitectures) + - [NotifyAccess](./systemd-service-config.md#615-notifyaccess) +7. Capabilities + - [AmbientCapabilities](./systemd-service-config.md#71-ambientcapabilities) + - [CapabilityBoundingSet](./systemd-service-config.md#72-capabilityboundingset) +8. System calls + - [SystemCallFilter](./systemd-service-config.md#81-systemcallfilter) + +
+ +--- + +## 1. Networking + + +### 1.1. PrivateNetwork + +[PrivateNetwork](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=) is useful for preventing the service from accessing the network. + +**Type**: *Boolean.* +**Default**: `false` +**Options**: +* `true` : Creates a new network namespace for the service. Only the loopback device "lo" is available in this namespace, other network devices are not accessible. +* `false` : The service will use the host's network namespace, it can access all the network devices available on the host. It can communicate over the network like any other process running on a host. + + +### 1.2. IPAccounting + +[IPAccounting](https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#IPAccounting=) helps in detecting unusual or unexpected network activity by a service. + +**Type**: *Boolean.* +**Default**: `false` +**Options**: +* `true`: Enables accounting for all IPv4 and IPv6 sockets created by the service: keeps track of the data sent and received by each socket in the service. +* `false`: Disables tracking of the sockets created by the service. + + +### 1.3. IPAddressAllow, IPAddressDeny + +[IPAddressAllow](https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#IPAddressAllow=)=ADDRESS[/PREFIXLENGTH]…, IPAddressDeny=ADDRESS[/PREFIXLENGTH]… + +Enables packet filtering on all IPv4 and IPv6 sockets created by the service. Useful for restricting/preventing a service from communicating only with certain IP addresses or networks. + +**Type**: *Space separated list of ip addresses and/or a symbolic name.* +**Default**: All IP addresses are allowed and no IP addresses are explicitly denied. +**Options**: +- *List of addresses*: Specify list of addresses allowed/denied. For example, `['192.168.1.8' '192.168.1.0/24']`. Any IP not explicitly allowed will be denied. +- *Symbolic Names*: Following symbolic names can also be used. + `any` : Any host (i.e., '0.0.0.0/0 ::/0'). + `localhost`: All addresses on the local loopback (i.e., '127.0.0.0/8 ::1/128'). + `link-local`: All link-local IP addresses(i.e., '169.254.0.0/16 fe80::/64'). + `multicast`: All IP multicasting addresses (i.e., 224.0.0.0/4 ff00::/8). + + +### 1.4. RestrictNetworkInterfaces + +[RestrictNetworkInterfaces](https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#RestrictNetworkInterfaces=) is used to control which network interfaces a service has access to. This helps isolate services from the network or restrict them to specific network interfaces, enhancing security and reducing potential risk. + +**Type**: *Space-separated list of network interface names.* +**Default**: The service can access to all available network interfaces unless other network restrictions are in place. +**Options**: +* Specify individual network interface names to restrict the service to using only those interfaces. +* Prefix an interface name with '~' to invert the restriction, i.e. denying access to that specific interface while allowing all others. + + +### 1.5. RestrictAddressFamilies + +[RestrictAddressFamilies](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=) is used to control which address families a service can use. This setting restricts the service's ability to open sockets using specific address families, such as `'AF_INET'` for IPv4, `'AF_INET6'` for IPv6, or others. It is a security feature that helps limit the service's network capabilities and reduces its exposure to network-related vulnerabilities. + +**Type**: List of address family names. +**Default**: If not configured, the service is allowed to use all available address families. +**Options**: +* **`none`**: Apply no restriction. +* **Specific Address Families**: Specify one or more address families that the service is allowed to use, for example, `'AF_INET'`, `'AF_INET6'`, `'AF_UNIX'`. +* **Inverted Restriction**: Prepend character '~' to an address family name to deny access to it while allowing all others, for example, `'~AF_INET'` would block IPv4 access. + +[Back to Top ⏫](./systemd-service-config.md#systemd-service-hardening) +--- + + +## 2. File System + +### 2.1 ProtectHome + +[ProtectHome](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=) is used to restrict a service's access to home directories. This security feature can be used either completely to block access to `/home`, `/root`, and `/run/user` or make them appear empty to the service, thereby protecting user data from unauthorized access by system services. + +**Type**: *Boolean or String.* +**Default**: `false` i.e. the service has full access to home directories unless restricted by some other mean. +**Options**: +* **`true`**: The service is completely denied access to home directories. +* **`false`**: The service has unrestricted access to home directories. +* **`read-only`**: The service can view the contents of home directories but cannot modify them. +* **`tmpfs`**: Mounts a temporary filesystem in place of home directories, ensuring the service cannot access or modify the actual user data. Adding the tmpfs option provides a flexible approach by creating a volatile in-memory filesystem where the service believes it has access to home but any changes it makes do not affect the actual data and are lost when the service stops. This is particularly useful for services that require a temporary space in a home. + + +### 2.2. ProtectSystem + +[ProtectSystem](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=) controls access to the system's root directory (`/`) and other essential system directories. This setting enhances security by restricting a service's ability to modify or access critical system files and directories. + +**Type**: *Boolean or String.* +**Default**: `full` (Equivalent to `true`). The service is restricted from modifying or accessing critical system directories. +**Options**: +* **`true`**: Mounts the directories `/usr/`, `/boot`, and `/efi` read-only for processes. +* **`full`**: Additionally mounts the `/etc/` directory read-only. +* **`strict`**: Mounts the entire file system hierarchy read-only, except for essential API file system subtrees like `/dev/`, `/proc/`, and `/sys/`. +* **`false`**: Allows the service unrestricted access to system directories. + +Using `true` or `full` is recommended for services that do not require access to system directories to enhance security and stability. + + +### 2.3. ProtectProc + +[ProtectProc](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectProc=) controls access to the `/proc` filesystem for a service. This setting enhances security by restricting a service's ability to view or manipulate processes and kernel information in the `/proc` directory. + +**Type**: *Boolean or String.* +**Default**: `default`. No restriction is imposed from viewing or manipulating processes and kernel information in `/proc`. +**Options**: +* **`noaccess`**: Restricts access to most process metadata of other users in `/proc`. +* **`invisible`**: Hides processes owned by other users from view in `/proc`. +* **`ptraceable`**: Hides processes that cannot be traced (`ptrace()`) by other processes. +* **`default`**: Imposes no restrictions on access or visibility to `/proc`. + + +### 2.4. ReadWritePaths, ReadOnlyPaths, InaccessiblePaths, ExecPaths, NoExecPaths + +[ReadWritePaths](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ReadWritePaths=) creates a new file system namespace for executed processes, enabling fine-grained control over file system access. + +* **ReadWritePaths=**: Paths listed here are accessible with the same access modes from within the namespace as from outside it. +* **ReadOnlyPaths=**: Allows reading from listed paths only; write attempts are refused even if file access controls would otherwise permit it. +* **InaccessiblePaths=**: Makes listed paths and everything below them in the file system hierarchy inaccessible to processes within the namespace. +* **NoExecPaths=**: Prevents execution of files from listed paths, overriding usual file access controls. Nest `ExecPaths=` within `NoExecPaths=` to selectively allow execution within directories otherwise marked non-executable. + +**Type**: *Space-separated list of paths.* +**Default**: No restriction to file system access until unless restricted by some other mechanism. +**Options**: +**Space separated list of paths** : Space-separated list of paths relative to the host's root directory. Symlinks are resolved relative to the root directory specified by `RootDirectory=` or `RootImage=`. + + +### 2.5. PrivateTmp + +[PrivateTmp](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=) uses a private, isolated `/tmp` directory for the service, enhancing security by preventing access to other processes' temporary files and ensuring data isolation. + +**Type**: *Boolean.* +**Default**: `false`. If not specified, the service shares the system `/tmp` directory with other processes. +**Options**: +* **`true`**: Enables private `/tmp` for the service, isolating its temporary files from other processes. +* **`false`**: The service shares the system `/tmp` directory with other processes. + +Additionally, when enabled, all temporary files created by a service in these directories will be automatically removed after the service is stopped. + + +### 2.6. PrivateMounts + +[PrivateMounts](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=) controls whether the service should have its mount namespace, isolating its mounts from the rest of the system. This setup ensures that any file system mount points created or removed by the unit's processes remain private to them and are not visible to the host. + +**Type**: *Boolean.* +**Default**: `false`. If not specified, the service shares the same mount namespace as other processes. +**Options**: +* **`true`**: Enables private mount namespace for the service, isolating its mounts from the rest of the system. +* **`false`**: The service shares the same mount namespace as other processes. + + +### 2.7. ProcSubset + +[ProcSubset](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProcSubset=) restricts the set of `/proc` entries visible to the service, enhancing security by limiting access to specific process information in the `/proc` filesystem. + +**Type**: *String.* +**Default**: `all`. If not specified, the service has access to all `/proc` entries. +**Options**: +* **`all`**: Allows the service access to all `/proc` entries. +* **`pid`**: Restricts the service to only its own process information (`/proc/self`, `/proc/thread-self/`). + +[Back to Top ⏫](./systemd-service-config.md#systemd-service-hardening) +--- + + +## 3. User Separation + +> **IMPORTANT:** Not applicable for the service runs as root. + + +### 3.1. PrivateUsers + +[PrivateUsers=](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=) controls whether the service should run with a private set of UIDs and GIDs, isolating the user and group databases used by the unit from the rest of the system, and creating a secure sandbox environment. The isolation reduces the privilege escalation potential of services. + +**Type**: *Boolean.* +**Default**: `false`. If not specified, the service runs with the same user and group IDs as other processes. +**Options**: +* **`true`**: Enables private user and group IDs for the service by creating a new user namespace, isolating them from the rest of the system. +* **`false`**: The service runs with the same user and group IDs as other processes. + + +### 3.2. DynamicUser + +[DynamicUser](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DynamicUser=) enables systemd to dynamically allocate a unique user and group ID (UID/GID) for the service at runtime, enhancing security and resource isolation. These user and group entries are managed transiently during runtime and are not added to `/etc/passwd` or `/etc/group`. + +**Type**: *Boolean.* +**Default**: `false`. If not specified, the service uses a static user and group ID defined in the service unit file or defaults to `root`. +**Options**: +* **`true`**: A UNIX user and group pair are dynamically allocated when the unit is started and released as soon as it is stopped. +* **`false`**: The service uses a static UID/GID defined in the service unit file or defaults to `root`. + +[Back to Top ⏫](./systemd-service-config.md#systemd-service-hardening) +--- + + +## 4. Devices + + +### 4.1. PrivateDevices + +[PrivateDevices](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=) controls whether the service should have access to device nodes in `/dev`. + +**Type**: *Boolean.* +**Default**: `false`. If not specified, the service has access to device nodes in `/dev`. +**Options**: +* **`true`**: Restricts the service's access to device nodes in `/dev` by creating a new `/dev/` mount for the executed processes and includes only pseudo devices such as `/dev/null`, `/dev/zero`, or `/dev/random`. Physical devices are not added to this mount. This setup is useful for disabling physical device access by the service. +* **`false`**: The service has access to device nodes in `/dev`. + + +### 4.2. DeviceAllow + +[DeviceAllow](https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#DeviceAllow=) specifies individual device access rules for the service, allowing fine-grained control over device permissions. + +**Type**: *Space-separated list of device access rules.* +**Default**: None. If not specified, the service does not have specific device access rules defined. +**Options**: +* Specify device access rules in the format: ` ` where `` can be `r` (read), `w` (write), or `m` (mknod, allowing creation of devices). + +[Back to Top ⏫](./systemd-service-config.md#systemd-service-hardening) +--- + + +## 5. Kernel + + +### 5.1. ProtectKernelTunables + +[ProtectKernelTunables](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=) controls whether the service is allowed to modify tunable kernel variables in `/proc/sys`, enhancing security by restricting access to critical kernel parameters. + +**Type**: *Boolean.* +**Default**: `true`. If not specified, the service is restricted from modifying kernel variables. +**Options**: +* **`true`**: Restricts the service from modifying the kernel variables accessible through paths like `/proc/sys/`, `/sys/`, `/proc/sysrq-trigger`, `/proc/latency_stats`, `/proc/acpi`, `/proc/timer_stats`, `/proc/fs`, and `/proc/irq`. These paths are made read-only to all processes of the unit. +* **`false`**: Allows the service to modify tunable kernel variables. + + +### 5.2. ProtectKernelModules + +[ProtectKernelModules](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=) controls whether the service is allowed to load or unload kernel modules, enhancing security by restricting module management capabilities. + +**Type**: *Boolean.* +**Default**: `true`. If not specified, the service is restricted from loading or unloading kernel modules. +**Options**: +* **`true`**: Restricts the service from loading or unloading kernel modules. It removes `CAP_SYS_MODULE` from the capability bounding set for the unit and installs a system call filter to block module system calls. `/usr/lib/modules` is also made inaccessible. +* **`false`**: Allows the service to load or unload kernel modules in a modular kernel. + + +### 5.3. ProtectKernelLogs + +[ProtectKernelLogs](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelLogs=) controls whether the service is allowed to access kernel log messages, enhancing security by restricting access to kernel logs. + +**Type**: *Boolean.* +**Default**: `false`. If not specified, the service is allowed to access kernel logs. +**Options**: +* **`trues`**: Restricts the service from accessing kernel logs from `/proc/kmsg` and `/dev/kmsg`. Enabling this option removes `CAP_SYSLOG` from the capability bounding set for the unit and installs a system call filter to block the syslog(2) system call. +* **`no`**: Allows the service to access kernel logs. + +[Back to Top ⏫](./systemd-service-config.md#systemd-service-hardening) +--- + + +## 6. Misc + +### 6.1. Delegate + +[Delegate](https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Delegate=) controls whether systemd should delegate further control of resource management to the service's own resource management settings. + +**Type**: *Boolean.* +**Default**: `true`. If not specified, systemd delegates control to the service's resource management settings. +**Options**: +* **`true`**: Enables delegation and activates all supported controllers for the unit, allowing its processes to manage them. +* **`false`**: Disables delegation entirely. Systemd retains control over resource management, potentially overriding the service's settings. + + +### 6.2. KeyringMode + +[KeyringMode](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=) specifies the handling mode for session keyrings by the service, controlling how it manages encryption keys and credentials. + +**Type**: *String.* +**Default**: `private`. If not specified, the service manages its session keyrings privately. +**Options**: +* **`private`**: The service manages its session keyrings privately. +* **`shared`**: The service shares its session keyrings with other services and processes. +* **`inherit`**: The service inherits session keyrings from its parent process or environment. + + +### 6.3. NoNewPrivileges + +[NoNewPrivileges](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=) controls whether the service and its children processes are allowed to gain new privileges (capabilities). + +**Type**: *Boolean.* +**Default**: `false`. If not specified, the service and its children's processes can gain new privileges. +**Options**: +- **`true`**: Prevents the service and its children processes from gaining new privileges. +- **`false`**: Allows the service and its children processes to gain new privileges. + +> [!IMPORTANT] +> Some configurations may override this setting and ignore its value. + +### 6.4. UMask + +[UMask](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=) + sets the file mode creation mask (umask) for the service, controlling the default permissions applied to newly created files and directories. + +**Type**: *Octal numeric value.* +**Default**: If not specified, inherits the default umask of the systemd service manager(0022). +**Example**: `UMask=027`. + + +### 6.5. ProtectHostname + +[ProtectHostname](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=) controls whether the service can modify its own hostname. + +**Type**: *Boolean.* +**Default**: `false`. +**Options**: +* **`true`**: Sets up a new UTS namespace for the executed processes. It prevents changes to the hostname or domainname. +* **`false`**: Allows the service to modify its own hostname. + + +### 6.6. ProtectClock + +[ProtectClock](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectClock=) controls whether the service is allowed to manipulate the system clock. + +**Type**: *Boolean.* +**Default**: `false`. +**Options**: +* **`true`**: Prevents the service from manipulating the system clock. It removes `CAP_SYS_TIME` and `CAP_WAKE_ALARM` from the capability bounding set for this unit. Also creates a system call filter to block calls that can manipulate the system clock. +* **`false`**: Allows the service to manipulate the system clock. + + +### 6.7. ProtectControlGroups + +[ProtectControlGroups](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=) controls whether the service is allowed to modify control groups (cgroups) settings. + +**Type**: *Boolean.* +**Default**: `false`. +**Options**: +* **`true`**: Prevents the service from modifying cgroups settings. Makes the Linux Control Groups (cgroups(7)) hierarchies accessible through `/sys/fs/cgroup/` read-only to all processes of the unit. +* **`false`**: Allows the service to modify cgroups settings. + + +### 6.8. RestrictNamespaces + +[RestrictNamespaces](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=) controls the namespace isolation settings for the service, restricting or allowing namespace access. + +**Type**: *Boolean* or *space-separated list of namespace type identifiers*. +**Default**: `false`. +**Options**: +* `false`: No restrictions on namespace creation and switching are imposed. +* `true`: Prohibits access to any kind of namespacing. +* Otherwise: Specifies a space-separated list of namespace type identifiers, which can include `cgroup`, `ipc`, `net`, `mnt`, `pid`, `user`, and `uts`. When the namespace identifier is prefixed with '~', it inverts the action. + + +### 6.9. LockPersonality + +[LockPersonality](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=) applies restriction on the service's ability to change its execution personality. + +**Type**: *Boolean.* +**Default**: `false`. +**Options**: +* **`true`**: Prevents the service from changing its execution personality. If the service runs in user mode or in system mode without the `CAP_SYS_ADMIN` capability (e.g., setting `User=`), enabling this option implies `NoNewPrivileges=yes`. +* **`false`**: Allows the service to change its execution personality. + + +### 6.10. MemoryDenyWriteExecute + +[MemoryDenyWriteExecute](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=) controls whether the service is allowed to execute code from writable memory pages. + +**Type**: *Boolean.* +**Default**: `false`. +**Options**: +* **`true`**: Prohibits attempts to create memory mappings that are writable and executable simultaneously, change existing memory mappings to become executable, or map shared memory segments as executable. This restriction is implemented by adding an appropriate system call filter. +* **`false`**: Allows the service to execute code from writable memory pages. + + +### 6.11. RestrictRealtime + +[RestrictRealtime](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=) controls whether the service is allowed to utilize real-time scheduling policies. + +**Type**: *Boolean.* +**Default**: `false`. +**Options**: +* **`true`**: Prevents the service from utilizing real-time scheduling policies. Refuses any attempts to enable realtime scheduling in processes of the unit. This restriction prevents access to realtime task scheduling policies such as `SCHED_FIFO`, `SCHED_RR`, or `SCHED_DEADLINE`. +* **`false`**: Allows the service to utilize real-time scheduling policies. + + +### 6.12. RestrictSUIDSGID + +[RestrictSUIDSGID](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=) controls whether the service is allowed to execute processes with SUID and SGID privileges. + +**Type**: *Boolean.* +**Default**: `false`. +**Options**: +* **`true`**: Prevents the service from executing processes with SUID and SGID privileges. Denies any attempts to set the set-user-ID (SUID) or set-group-ID (SGID) bits on files or directories. These bits are used to elevate privileges and allow users to acquire the identity of other users. +* **`false`**: Allows the service to execute processes with SUID and SGID privileges. + + +### 6.13. RemoveIPC + +[RemoveIPC](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=) controls whether to remove inter-process communication (IPC) resources associated with the service upon its termination. + +**Type**: *Boolean.* +**Default**: `false`. +**Options**: +* **`true`**: Removes IPC resources (**System V** and **POSIX IPC** objects) associated with the service upon its termination. This includes IPC objects such as message queues, semaphore sets, and shared memory segments. +* **`false`**: Retains IPC resources associated with the service after its termination. + + +### 6.14. SystemCallArchitectures + +[SystemCallArchitectures](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=) specifies the allowed system call architectures for the service to include in system call filter. + +**Type**: *Space-separated list of architecture identifiers.* +**Default**: Empty list. No filtering is applied. +**Options**: +* *List of architectures*: Processes of this unit will only be allowed to call native system calls and system calls specific to the architectures specified in the list. e.g. `native`, `x86`, `x86-64` or `arm64` etc. + + +### 6.15. NotifyAccess + +[NotifyAccess](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html#NotifyAccess=) specifies how the service can send service readiness notification signals. + +**Type**: *Access specifier string.* +**Default**: `none`. +**Options**: +* `none` (default): No daemon status updates are accepted from the service processes; all status update messages are ignored. +* `main`: Allows sending signals using the main process identifier (PID). +* `exec`: Only service updates sent from any main or control processes originating from one of the `Exec*=` commands are accepted. +* `all`: Allows sending signals using any process identifier (PID). + +[Back to Top ⏫](./systemd-service-config.md#systemd-service-hardening) +--- + + +## 7. Capabilities + + +### 7.1. AmbientCapabilities + +[AmbientCapabilities](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=) specifies which capabilities to include in the ambient capability set for the service, which are inherited by all processes within the service. + +**Type**: *Space-separated list of capabilities.* +**Default**: Processes inherit ambient capabilities from their parent process or the systemd service manager unless explicitly set. +**Options**: +* *List of capabilities*: Specifies the capabilities that are set as ambient for all processes within the service. + +This option can be specified multiple times to merge capability sets: +* If capabilities are listed without a prefix, those capabilities are included in the ambient capability set. +* If capabilities are prefixed with "~", all capabilities except those listed are included (inverted effect). +* Assigning the empty string (`""`) resets the ambient capability set to empty, overriding all prior settings. + + +### 7.2. CapabilityBoundingSet + +[CapabilityBoundingSet](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=) specifies the bounding set of capabilities for the service, limiting the capabilities available to processes within the service. + +**Type**: *Space-separated list of capabilities.* +**Default**: If not explicitly specified, the bounding set of capabilities is determined by systemd defaults or the system configuration. +**Options**: +* *List of capabilities*: Specifies the capabilities that are allowed for processes within the service. If capabilities are prefixed with "~", all capabilities except those listed are included (inverted effect). + + +**Capability** | **Description** +--- | -- +**CAP_AUDIT_CONTROL** | Allows processes to control kernel auditing behavior, including enabling and disabling auditing, and changing audit rules. +**CAP_AUDIT_READ** | Allows processes to read audit log via unicast netlink socket. +**CAP_AUDIT_WRITE** | Allows processes to write records to kernel auditing log. +**CAP_BLOCK_SUSPEND** | Allows processes to prevent the system from entering suspend mode. +**CAP_CHOWN** | Allows processes to change the ownership of files. +**CAP_DAC_OVERRIDE** | Allows processes to bypass file read, write, and execute permission checks. +**CAP_DAC_READ_SEARCH** | Allows processes to bypass file read permission checks and directory read and execute permission checks. +**CAP_FOWNER** | Allows processes to bypass permission checks on operations that normally require the filesystem UID of the file to match the calling process's UID. +**CAP_FSETID** | Allows processes to set arbitrary process and file capabilities. +**CAP_IPC_LOCK** | Allows processes to lock memory segments into RAM. +**CAP_IPC_OWNER** | Allows processes to perform various System V IPC operations, such as message queue management and shared memory management. +**CAP_KILL** | Allows processes to send signals to arbitrary processes. +**CAP_LEASE** | Allows processes to establish leases on open files. +**CAP_LINUX_IMMUTABLE** | Allows processes to modify the immutable and append-only flags of files. +**CAP_MAC_ADMIN** | Allows processes to perform MAC configuration changes. +**CAP_MAC_OVERRIDE** | Bypasses Mandatory Access Control (MAC) policies. +**CAP_MKNOD** | Allows processes to create special files using mknod(). +**CAP_NET_ADMIN** | Allows processes to perform network administration tasks, such as configuring network interfaces, setting routing tables, etc. +**CAP_NET_BIND_SERVICE** | Allows processes to bind to privileged ports (ports below 1024). +**CAP_NET_BROADCAST** | Allows processes to transmit packets to broadcast addresses. +**CAP_NET_RAW** | Allows processes to use raw and packet sockets. +**CAP_SETGID** | Allows processes to change their GID to any value. +**CAP_SETFCAP** | Allows processes to set any file capabilities. +**CAP_SETPCAP** | Allows processes to set the capabilities of other processes. +**CAP_SETUID** | Allows processes to change their UID to any value. +**CAP_SYS_ADMIN** | Allows processes to perform a range of system administration tasks, such as mounting filesystems, configuring network interfaces, loading kernel modules, etc. +**CAP_SYS_BOOT** | Allows processes to reboot or shut down the system. +**CAP_SYS_CHROOT** | Allows processes to use chroot(). +**CAP_SYS_MODULE** | Allows processes to load and unload kernel modules. +**CAP_SYS_NICE** | Allows processes to increase their scheduling priority. +**CAP_SYS_PACCT** | Allows processes to configure process accounting. +**CAP_SYS_PTRACE** | Allows processes to trace arbitrary processes using ptrace(). +**CAP_SYS_RAWIO** | Allows processes to perform I/O operations directly to hardware devices. +**CAP_SYS_RESOURCE** | Allows processes to override resource limits. +**CAP_SYS_TIME** | Allows processes to set system time and timers. +**CAP_SYS_TTY_CONFIG** | Allows processes to configure tty devices. +**CAP_WAKE_ALARM** | Allows processes to use the RTC wakeup alarm. + +[Back to Top ⏫](./systemd-service-config.md#systemd-service-hardening) +--- + + +## 8. System Calls + + +### 8.1. SystemCallFilter + +[SystemCallFilter](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=) specifies a system call filter for the service, restricting the types of system calls that processes within the service can make. + +**Type**: *Space-separated list of system calls.* +**Default**: If not explicitly specified, there are no restrictions imposed by systemd on system calls. +**Options**: +* *List of system calls*: Specifies the allowed system calls for processes within the service. If the list begins with "~", the effect is inverted, meaning only the listed system calls will result in termination. + +> [!TIP] +> Predefined sets of system calls are available, starting with "@" followed by the name of the set. + + +**Filter Set** | **Description** +--- | --- +**@clock** | Allows clock and timer-related system calls, such as clock_gettime, nanosleep, etc. This is essential for time-related operations. +**@cpu-emulation** | Allows CPU emulation-related system calls, typically used by virtualization software. +**@debug** | Allows debug-related system calls, which are often used for debugging purposes and may not be necessary for regular operations. +**@keyring** | Allows keyring-related system calls, which are used for managing security-related keys and keyrings. +**@module** | Allows module-related system calls, which are used for loading and unloading kernel modules. This can be restricted to prevent module loading for security purposes. +**@mount** | Allows mount-related system calls, which are essential for mounting and unmounting filesystems. +**@network** | Allows network-related system calls, which are crucial for networking operations such as socket creation, packet transmission, etc. +**@obsolete** | Allows obsolete system calls, which are no longer in common use and are often deprecated. +**@privileged** | Allows privileged system calls, which typically require elevated privileges or are potentially risky if misused. +**@raw-io** | Allows raw I/O-related system calls, which provide direct access to hardware devices. This can be restricted to prevent unauthorized access to hardware. +**@reboot** | Allows reboot-related system calls, which are necessary for initiating system reboots or shutdowns. +**@swap** | Allows swap-related system calls, which are used for managing swap space. +**@syslog** | Allows syslog-related system calls, which are used for system logging. +**@system-service** | Allows system service-related system calls, which are used for managing system services. +**@timer** | Allows timer-related system calls, which are essential for setting and managing timers. + + +[Back to Top ⏫](./systemd-service-config.md#systemd-service-hardening) +--- \ No newline at end of file diff --git a/docs/src/release_notes/ghaf-23.05.md b/docs/src/release_notes/ghaf-23.05.md index a2480a55e..f0b66e0da 100644 --- a/docs/src/release_notes/ghaf-23.05.md +++ b/docs/src/release_notes/ghaf-23.05.md @@ -30,6 +30,7 @@ This is the first release of Ghaf including support for: * Element, a Matrix-based chat client (on the host) * the Google Android look-alike (GALA) application +> [!WARNING] > Ghaf Framework is under active development, some of the features may not be stable. diff --git a/docs/src/release_notes/ghaf-23.06.md b/docs/src/release_notes/ghaf-23.06.md index fb6b3f8a7..9cb916870 100644 --- a/docs/src/release_notes/ghaf-23.06.md +++ b/docs/src/release_notes/ghaf-23.06.md @@ -27,7 +27,7 @@ The following target hardware is supported by this release: * the development status: . * SLSA v1.0 level provenance file included. * Ghaf version information (query). -* NixOS is updated to 23.05: [NixOS 23.05 released!](https://discourse.nixos.org/t/nixos-23-05-released/28649) +* NixOS is updated to NixOS 23.05: [NixOS 23.05 released!](https://discourse.nixos.org/t/nixos-23-05-released/28649) ## Bug Fixes diff --git a/docs/src/release_notes/ghaf-23.09.md b/docs/src/release_notes/ghaf-23.09.md index 96fd18414..91edadf68 100644 --- a/docs/src/release_notes/ghaf-23.09.md +++ b/docs/src/release_notes/ghaf-23.09.md @@ -31,8 +31,8 @@ The following target hardware is supported by this release: * Modularization of the Ghaf framework: [Ghaf as Library: Templates](../ref_impl/ghaf-based-project.md). * NVIDIA Jetson Orin NX Ethernet passthrough. * Lenovo X1 Carbon Gen 11: - * Graphics passthrough to GUIVM. - * Launching Application VMs through GUIVM (Chromium, Gala, and Zathura). + * Graphics passthrough to GUI VM. + * Launching Application VMs through GUI VM (Chromium, Gala, and Zathura). * Paravirtualized audio. * Webcam passthrough. * Touchpad passthrough. @@ -52,7 +52,7 @@ Fixed bugs that were in the ghaf-23.06 release: | Issue | Status | Comments | |-----------------|-------------|--------------------------------------| -| Chromium AppVM does not boot up on X1 | In Progress | Intermittent timing issue, under investigation. | +| Chromium App VM does not boot up on X1 | In Progress | Intermittent timing issue, under investigation. | | The GALA app does not work | In Progress | Will be fixed in the next release. | | Shutdown or reboot of Lenovo X1 takes a lot of time (7 minutes) | In Progress | Advice: be patient or, if in hurry, press power key for 15 sec. | | Copy and paste text from or to Chromium AppVM does not work | In Progress | | diff --git a/docs/src/release_notes/ghaf-23.12.md b/docs/src/release_notes/ghaf-23.12.md index 46b375e3b..8cfdce420 100644 --- a/docs/src/release_notes/ghaf-23.12.md +++ b/docs/src/release_notes/ghaf-23.12.md @@ -29,7 +29,7 @@ The following target hardware is supported by this release: * CLI-based installer. * Lenovo X1 Carbon Gen 11: * Configurable PCI and USB devices passthrough. - * Network Manager: support from GUIVM to NETVM. + * Network Manager: support from GUI VM to Net VM. * Windows VM support. * Added Ghaf icons and the background image. * Secure Boot is disabled by default. @@ -66,7 +66,7 @@ Fixed bugs that were in the ghaf-23.09 release: | Time synchronization between host and VMs does not work in all scenarios | In Progress | Under investigation. | | The taskbar disappears after the external display is disconnected from Lenovo X1 | In Progress | Under investigation. | | Closing and re-opening a deck lid of a X1 laptop with running Ghaf causes instability | In Progress | Workaround: keep a deck lid of a laptop open while working with Ghaf. | -| Applications do not open from icons when netvm is restarted | In Progress | Workaround: Restart AppVMs. | +| Applications do not open from icons when net-vm is restarted | In Progress | Workaround: Restart App VMs. | ## Environment Requirements diff --git a/docs/src/release_notes/ghaf-24.03.md b/docs/src/release_notes/ghaf-24.03.md index 6438ac613..fb597a3fc 100644 --- a/docs/src/release_notes/ghaf-24.03.md +++ b/docs/src/release_notes/ghaf-24.03.md @@ -6,9 +6,9 @@ # Release ghaf-24.03 -## Release Branch +## Release Tag - + ## Supported Hardware @@ -58,9 +58,9 @@ Fixed bugs that were in the ghaf-23.12 release: | Cannot log in to the Element chat with a Google account | In Progress | Workaround for x86: create a user specifically for Element. | | Windows launcher application does not work on AGX | In Progress | Workaround: launch a Windows VM from the command line. | | Time synchronization between host and VMs does not work in all scenarios | In Progress | Under investigation. | -| Closing and re-opening a deck lid of a X1 laptop with running Ghaf causes instability | In Progress | Workaround: keep a deck lid of a laptop open while working with Ghaf. | +| Closing and reopening a deck lid of a Lenovo ThinkPad X1 laptop with Ghaf running causes instability | In Progress | Workaround: keep a deck lid of a laptop open while working with Ghaf. | | Applications do not open from icons when netvm is restarted | In Progress | Workaround: restart AppVMs. | -| Cannot connect to a hidden Wi-Fi network from GUI | In Progress | Workaround: connect with SSH to netvm and run the command `nmcli dev wifi connect SSID password PASSWORD hidden yes`. | +| Cannot connect to a hidden Wi-Fi network from GUI | In Progress | Workaround: connect with SSH to a netvm and run the command: `nmcli dev wifi connect SSID password PASSWORD hidden yes`. | ## Environment Requirements @@ -79,7 +79,7 @@ Download the required image and use the following instructions: | ghaf-24.03_Generic_x86.tar.xz | [Running Ghaf Image for x86 Computer](../ref_impl/build_and_run.md#running-ghaf-image-for-x86-computer) | | ghaf-24.03_Lenovo_X1_Carbon_Gen11.tar.xz | [Running Ghaf Image for Lenovo X1](../ref_impl/build_and_run.md#running-ghaf-image-for-lenovo-x1) | | ghaf-24.03_Nvidia_Orin_AGX_cross-compiled-no-demoapps.tar.xz[^note], ghaf-24.03_Nvidia_Orin_AGX_cross-compiled.tar.xz, ghaf-24.03_Nvidia_Orin_AGX_native-build.tar.xz | [Ghaf Image for NVIDIA Jetson Orin AGX](../ref_impl/build_and_run.md#ghaf-image-for-nvidia-jetson-orin-agx) | -| ghaf-24.03_Nvidia_Orin_NX_cross-compiled-no-demoapps[^note].tar.xz, ghaf-24.03_Nvidia_Orin_NX_cross-compiled.tar.xz, ghaf-24.03_Nvidia_Orin_NX_native-build.tar.xz | [Ghaf Image for NVIDIA Jetson Orin AGX](../ref_impl/build_and_run.md#ghaf-image-for-nvidia-jetson-orin-agx) | +| ghaf-24.03_Nvidia_Orin_NX_cross-compiled-no-demoapps[^note1].tar.xz, ghaf-24.03_Nvidia_Orin_NX_cross-compiled.tar.xz, ghaf-24.03_Nvidia_Orin_NX_native-build.tar.xz | [Ghaf Image for NVIDIA Jetson Orin AGX](../ref_impl/build_and_run.md#ghaf-image-for-nvidia-jetson-orin-agx) | | ghaf-24.03_PolarFire_RISC-V.tar.xz | [Building Ghaf Image for Microchip Icicle Kit](../ref_impl/build_and_run.md#building-ghaf-image-for-microchip-icicle-kit) | -[^note] no-demoapps images do not include Chromium, Zathura, and GALA applications. \ No newline at end of file +[^note1] no-demoapps images do not include Chromium, Zathura, and GALA applications. \ No newline at end of file diff --git a/docs/src/release_notes/ghaf-24.06.md b/docs/src/release_notes/ghaf-24.06.md new file mode 100644 index 000000000..059c839cf --- /dev/null +++ b/docs/src/release_notes/ghaf-24.06.md @@ -0,0 +1,93 @@ + + +# Release ghaf-24.06 + + +## Release Tag + + + + +## Supported Hardware + +The following target hardware is supported by this release: + +* NVIDIA Jetson AGX Orin +* NVIDIA Jetson Orin NX +* Generic x86 (PC) +* Polarfire Icicle Kit +* Lenovo ThinkPad X1 Carbon Gen 11 +* Lenovo ThinkPad X1 Carbon Gen 10 +* NXP i.MX 8M Plus + + +## What is New in ghaf-24.06 + +* Added support for NXP i.MX 8M Plus. +* NixOS is updated to [NixOS 24.05](https://nixos.org/blog/announcements/2024/nixos-2405/) further to nixos-unstable. +* labwc is used as a default compositor on all platforms. Weston is no longer supported. +* Static networking with external DNS server support only. Internal DHCP and DNS are removed. + * This affects all new guest VM networking. + * Windows VM must be configured with static IP and DNS. +* Lenovo X1 Carbon Gen 10/11: + * Image compression uses the [Zstandard (zstd)](https://github.com/facebook/zstd) algorithm. + * Initial vTPM implementation for Application VMs is added. + * Audio VM with [PipeWire](https://gitlab.freedesktop.org/pipewire/pipewire) backend and [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/) TCP remote communications layer. + * Multimedia function key passthrough. + * Initial implementation of [IDS VM](../architecture/adr/idsvm.md) as a defensive network mechanism. + * Support for [Element](https://element.io/) chat application. + * GPS location sharing through the Element application. + * [AppFlowy](https://github.com/AppFlowy-IO/AppFlowy) uses the [Flutter](https://github.com/flutter) application framework. +* NVIDIA Jetson Orin NX: + * UARTI passthrough. + * The Jetpack baseline software updates and fixes. +* Further refactoring and modularization of Ghaf Framework. +* Development, testing, and performance tooling improvements. + + +## Bug Fixes + +Fixed bugs that were in the ghaf-24.03 release: + +* Icons do not launch applications when a netvm is restarted. +* Closing and reopening a deck lid of a Lenovo ThinkPad X1 laptop with Ghaf running causes instability. + + +## Known Issues and Limitations + +| Issue | Status | Comments | +|-----------------|-------------|--------------------------------------| +| Cannot log in to the Element chat with a Google account | In Progress | Workaround for x86: create a user specifically for Element. | +| Windows launcher application does not work on AGX | In Progress | Workaround: launch a Windows VM from the command line. | +| Time synchronization between host and VMs does not work in all scenarios | In Progress | Under investigation. | +| Applications do not open from icons when netvm is restarted | In Progress | Workaround: restart AppVMs. | +| Cannot connect to a hidden Wi-Fi network from GUI | In Progress | Workaround: connect with SSH to a netvm and run the command: `nmcli dev wifi connect SSID password PASSWORD hidden yes`. | +| NVIDIA Jetson AGX Orin and NVIDIA Jetson Orin NX: cannot make voice calls using the Element application | In Progress | Under investigation. | +| The Element application cannot find a camera | In Progress | Under investigation. | + + +## Environment Requirements + +There are no specific requirements for the environment with this release. + + +## Installation Instructions + +Released images are available at [vedenemo.dev/files/releases/ghaf_24.06/](https://vedenemo.dev/files/releases/ghaf_24.06/). + +Download the required image and use the following instructions: + +| Release Image | Build and Run | +|-------------------------|--------------------| +| ghaf-24.06_Generic_x86.tar.xz | [Running Ghaf Image for x86 Computer](../ref_impl/build_and_run.md#running-ghaf-image-for-x86-computer) | +| ghaf-24.06_Lenovo_X1_Carbon_Gen11.tar.xz | [Running Ghaf Image for Lenovo X1](../ref_impl/build_and_run.md#running-ghaf-image-for-lenovo-x1) | +| ghaf-24.06_Nvidia_Orin_AGX_cross-compiled.tar.xz, ghaf-24.06_Nvidia_Orin_AGX_native-build.tar.xz, ghaf-24.06_Nvidia_Orin_NX_cross-compiled.tar.xz, ghaf-24.06_Nvidia_Orin_NX_native-build.tar.xz | [Ghaf Image for NVIDIA Jetson Orin AGX](../ref_impl/build_and_run.md#ghaf-image-for-nvidia-jetson-orin-agx) | +| ghaf-24.06_PolarFire_RISC-V.tar.xz | [Building Ghaf Image for Microchip Icicle Kit](../ref_impl/build_and_run.md#building-ghaf-image-for-microchip-icicle-kit) | + + + diff --git a/docs/src/release_notes/release_notes.md b/docs/src/release_notes/release_notes.md index bd273a255..0d400d658 100644 --- a/docs/src/release_notes/release_notes.md +++ b/docs/src/release_notes/release_notes.md @@ -12,6 +12,7 @@ Release numbering scheme: *ghaf-yy.mm*. ## In This Chapter +- [Release ghaf-24.06](../release_notes/ghaf-24.06.md) - [Release ghaf-24.03](../release_notes/ghaf-24.03.md) - [Release ghaf-23.12](../release_notes/ghaf-23.12.md) - [Release ghaf-23.09](../release_notes/ghaf-23.09.md) diff --git a/docs/src/scenarios/run_cuttlefish.md b/docs/src/scenarios/run_cuttlefish.md index f69ddc73f..2d48aebf6 100644 --- a/docs/src/scenarios/run_cuttlefish.md +++ b/docs/src/scenarios/run_cuttlefish.md @@ -17,6 +17,7 @@ You can run Android as a VM on Ghaf for testing and development purposes using N * For NVIDIA Jetson Orin AGX (ARM64): [cvd-host_package.tar.gz](https://ci.android.com/builds/submitted/9970479/aosp_cf_arm64_phone-userdebug/latest/cvd-host_package.tar.gz) and [aosp_cf_arm64_phone-img-9970479.zip](https://ci.android.com/builds/submitted/9970479/aosp_cf_arm64_phone-userdebug/latest/aosp_cf_arm64_phone-img-9970479.zip) * For Generic x86: [cvd-host_package.tar.gz](https://ci.android.com/builds/submitted/9970479/aosp_cf_x86_64_phone-userdebug/latest/cvd-host_package.tar.gz) and [aosp_cf_x86_64_phone-img-9970479.zip](https://ci.android.com/builds/submitted/9970479/aosp_cf_x86_64_phone-userdebug/latest/aosp_cf_x86_64_phone-img-9970479.zip) + > [!NOTE] > Download a host package from the same build as the image. 2. Make sure Internet connection is working in Ghaf. If the system gets an IP address but the DNS server is not responding, set the correct date and time. diff --git a/docs/src/scenarios/run_win_vm.md b/docs/src/scenarios/run_win_vm.md index 9ffe3a643..c1909f2f0 100644 --- a/docs/src/scenarios/run_win_vm.md +++ b/docs/src/scenarios/run_win_vm.md @@ -22,7 +22,8 @@ You can run Windows 11 in a VM on Ghaf with NVIDIA Jetson Orin AGX (ARM64) or Ge sudo mkdir /mnt sudo mount /dev/sda /mnt ``` - > **WARNING:** [For NVIDIA Jetson Orin AGX] Make sure to use a fresh VHDX image file that was not booted in another environment before. + > [!WARNING] + > [For NVIDIA Jetson Orin AGX] Make sure to use a fresh VHDX image file that was not booted in another environment before. ## Running Windows 11 in VM @@ -38,7 +39,8 @@ You can run Windows 11 in a VM on Ghaf with NVIDIA Jetson Orin AGX (ARM64) or Ge 2. Windows 11 requires Internet access to finish the setup. To boot the VM without an Internet connection, open cmd with Shift+F10 and type `OOBE\BYPASSNRO`. After the configuration restart click “I don’t have internet“ to skip the Internet connection step and continue the installation. - > TIP: If after pressing Shift+F10 the command window is not displayed, try to switch between opened windows by using Alt+Tab. + > [!TIP] + > If after pressing Shift+F10 the command window is not displayed, try to switch between opened windows by using Alt+Tab. #### Running Windows 11 in VM on Generic x86 Device @@ -63,7 +65,8 @@ Do the following: * Name: `BypassTPMCheck`, value `1`. * Name: `BypassSecureBootCheck`, value `1`. - > TIP: [For Ghaf running on a laptop] If after pressing Shift+F10 the command window is not displayed, try again with the Fn key (Shift+Fn+F10) or switch between opened windows by using Alt+Tab. + > [!TIP] + > [For Ghaf running on a laptop] If after pressing Shift+F10 the command window is not displayed, try again with the Fn key (Shift+Fn+F10) or switch between opened windows by using Alt+Tab. 4. Install Windows 11 in the VM. 5. Windows 11 requires Internet access to finish the setup. To boot the VM without an Internet connection, open cmd with Shift+F10 and type `OOBE\BYPASSNRO`. After the configuration restart click “I don’t have internet“ to skip the Internet connection step and continue the installation. diff --git a/docs/src/scs/ci-cd-system.md b/docs/src/scs/ci-cd-system.md index 72a52d3b2..75e427bfb 100644 --- a/docs/src/scs/ci-cd-system.md +++ b/docs/src/scs/ci-cd-system.md @@ -9,6 +9,7 @@ Ghaf Framework uses a CI/CD (Continuous Integration and Continuous Delivery) app Our goal is to have the ability to deploy code quickly and safely: once a build is deployed, the next build undergoes testing, while the latest build is being coded. +> [!IMPORTANT] > Currently, Continuous Deployment is not set up. diff --git a/docs/src/scs/pki.md b/docs/src/scs/pki.md index a04659cba..82023175c 100644 --- a/docs/src/scs/pki.md +++ b/docs/src/scs/pki.md @@ -44,7 +44,8 @@ The following HSM solutions are considered for the Ghaf project: The following table provides feature comparison of the proposed solutions: ->Since the feature list is quite extensive, the table is limited to the features that are either planned to be used in Ghaf or might benefit the project in the future. +> [!IMPORTANT] +> Since the feature list is quite extensive, the table is limited to the features that are either planned to be used in Ghaf or might benefit the project in the future. | Feature | YubiHSM 2 | NitrokeyHSM2 | SoftHSMv2 | BreadboardHSM | |------------------------------|--------------|--------------|--------------|---------------| diff --git a/docs/src/technologies/hypervisor_options.md b/docs/src/technologies/hypervisor_options.md index 985e37025..3d83076ec 100644 --- a/docs/src/technologies/hypervisor_options.md +++ b/docs/src/technologies/hypervisor_options.md @@ -91,7 +91,12 @@ microvm.qemu.extraArgs = [ "--option 1 --option 2" ]; microvm may not supply parameters for all possible options as adding specific devices. Processing of all microvm configuration options is done in the mentioned above hypervisor’s runner .nix file. -The runners support the ``extraArgs`` parameter. It allows setting any option in QEMU command line invocation. Its value is a list of strings. In this example the following ``extraArgs`` definition: +The runners support the ``extraArgs`` parameter. It allows setting any option in QEMU command line invocation. Its value is a list of strings. + +> [!IMPORTANT] +> Support for the crosvm’s ``extraArgs`` parameter was added on April 7, 2023. Make sure to verify that your ``flakes.lock`` file refers to the proper version. + +In this example the following ``extraArgs`` definition: ``` microvm.qemu.extraArgs = [ @@ -106,5 +111,3 @@ results in the generated command line parameters: '-object memory-backend-file,id=mem1,mem-path=/dev/shm/virtio_pmem.img' '-device v irtio-pmem-pci,memdev=mem1,id=nv1' ``` - -> Support for the crosvm’s ``extraArgs`` parameter was added on April 7, 2023. Make sure to verify that your ``flakes.lock`` file refers to the proper version. diff --git a/docs/src/technologies/nvidia_agx_pt_pcie.md b/docs/src/technologies/nvidia_agx_pt_pcie.md index fbc7b3f5a..7c0731a3f 100644 --- a/docs/src/technologies/nvidia_agx_pt_pcie.md +++ b/docs/src/technologies/nvidia_agx_pt_pcie.md @@ -16,7 +16,7 @@ There are two (or actually three) PCIe slots in the Jetson AGX Orin board: * The other slot is a [smaller M.2 slot](#pcie-m2-slot) that is located at the bottom of the board. By default, the slot is in use of the included Wi-Fi and Bluetooth module. * The third slot is actually an [NVMe slot](#pcie-m2-nvme-2247-for-ssd) which can be used to add an NVMe SSD to the board. -> For more information on the board's connections details, see the [Hardware Layout](https://developer.nvidia.com/embedded/learn/jetson-agx-orin-devkit-user-guide/developer_kit_layout.html) section of the Jetson AGX Orin Developer Kit User Guide. +For more information on the board's connections details, see the [Hardware Layout](https://developer.nvidia.com/embedded/learn/jetson-agx-orin-devkit-user-guide/developer_kit_layout.html) section of the Jetson AGX Orin Developer Kit User Guide. When using one of the slots: @@ -28,11 +28,13 @@ When using one of the slots: The full-size PCIe connector is under the black plastic cover on one of the sides of the device. The cover is held in place with a fairly strong magnet. There is a small connector ribbon and a few delicate wires going from the board internals to a Wi-Fi antenna on the cover. -> **TIP:** Make sure to remove the cover carefully for not ripping the whole cover off along with the antenna cables. +> [!IMPORTANT] +> Make sure to remove the cover carefully for not ripping the whole cover off along with the antenna cables. The PCIe slot is simular to one inside a desktop computer. One key difference: the Jetson AGX Orin board has limited 12V power output capabilities and can only output a maximum of 40W power to its PCIe slot. Regular desktop PCIe slot can output 75W at 12V so some more power-hungry PCIe cards [^note1] may not work with the Jetson AGX Orin board. There may also be a risk of damaging the board if a card tries to pull too much power from the PCIe socket. -> **TIP:** We recommend to check carefully the power requirements of a device before turning the device on. +> [!IMPORTANT] +> We recommend to check carefully the power requirements of a device before turning the device on. A good rule of thumb might be if the device has a cooler to actively cool it down then some care should be taken before starting to use the card. Some trials have been done with GPU devices that use at maximum 30-34W power. The devices seem to work well in Jetson AGX Orin, but it is difficult to say how much power the card actually pulls from the slot at any given time. No real performance or stress tests have been done but under usual GUI and simple 3d application usage the cards (NVIDIA Quadro P1000 and NVIDIA Quadro T600) seem to work fine. @@ -114,6 +116,7 @@ You can also check the kernel logs to know which device belongs to which VFIO IO After binding a device to VFIO, you can access the device in a VM. To do so, use a command line argument (as in the example) for the PCI device to pass through to QEMU. +> [!NOTE] > It does not matter which VFIO node ID was assigned to the device earlier, as long as all the devices with the same VFIO node are passed through, and none of the devices in the same group is left behind. The QEMU command line argument for passthrough uses the PCIe device ID as identifier for the devices. Each diff --git a/docs/src/technologies/nvidia_agx_pt_uart.md b/docs/src/technologies/nvidia_agx_pt_uart.md index 6c7c30adb..459241248 100644 --- a/docs/src/technologies/nvidia_agx_pt_uart.md +++ b/docs/src/technologies/nvidia_agx_pt_uart.md @@ -31,11 +31,12 @@ The following table describes the UART units mapping and connections: | uarti: serial@31d0000 | UART5 | UART2 | USB Debug ttyACM1 | | uartj: serial@c270000 | Not mapped | | | -Notes: +In this table: * The first column shows how the UART units are defined in the UART device tree file *tegra234-soc-uart.dtsi* [^note1]. * The second and third columns show the CPU and SoC pin connections. Note that for UART2 and UART5 these are swapped. The pin mapping configuration is described in the file *tegra234-mb1-bct-pinmux-p3701-0000.dtsi* [^note2]. This device tree file is automatically generated by the macro Excel file *Jetson_AGX_Orin_Series_Pinmux_Config_Template_1.5.xlsm* which is available at the official Jetson Download Center as the *Jetson AGX Orin Series Pinmux*. * The last column describes where the UART units are connected to the exterior. +> [!NOTE] > Only two UART units are connected to the micro USB debug interface. The UART7 is not connected by default but it can be connected to the debug interface ttyACM1 by swapping the fuse resistors (see Debug MCU page 7 on P3737_A04_Concept_schematics.pdf [^note3]). @@ -110,7 +111,7 @@ Add the passthrough devices inside the platform node to this device tree: }; ``` -> In this example, the *uarti* node was added to the platform node. For this node the interrupt number was replaced to 0x70 and reg address to the one that was obtained from the QEMU monitor command: *info mtree -f*. +In this example, the *uarti* node was added to the platform node. For this node the interrupt number was replaced to 0x70 and the reg address to the one that was obtained from the QEMU monitor command: *info mtree -f*. ## Starting Guest VM diff --git a/docs/src/technologies/nvidia_virtualization_bpmp.md b/docs/src/technologies/nvidia_virtualization_bpmp.md index 235b1f313..bc8e537e8 100644 --- a/docs/src/technologies/nvidia_virtualization_bpmp.md +++ b/docs/src/technologies/nvidia_virtualization_bpmp.md @@ -28,14 +28,15 @@ The current implementation includes a host configuration for the UARTA passthrou 1. Enable NVIDIA BPMP virtualization on a Ghaf host for an NVIDIA Jetson-target using the following configuration options: -```nix - hardware.nvidia = { - virtualization.enable = true; - passthroughs.uarta.enable = true; -}; -``` + ```nix + hardware.nvidia = { + virtualization.enable = true; + passthroughs.uarta.enable = true; + }; + ``` -> **IMPORTANT:** These options are integrated to [NVIDIA Jetson Orin targets](https://github.com/tiiuae/ghaf/blob/main/targets/nvidia-jetson-orin/default.nix) but disabled by default until the implementation is finished. + > [!IMPORTANT] + > These options are integrated to [NVIDIA Jetson Orin targets](https://github.com/tiiuae/ghaf/blob/main/targets/nvidia-jetson-orin/default.nix) but disabled by default until the implementation is finished. 2. Build the target and boot the image. You can write the image to an SSD for testing with a recent NVIDIA UEFI FW. @@ -47,56 +48,58 @@ The current implementation includes a host configuration for the UARTA passthrou 1. Check the `bpmp-host` device: -``` -[ghaf@ghaf-host:~]$ ls /dev | grep bpmp-host -bpmp-host -``` + ``` + [ghaf@ghaf-host:~]$ ls /dev | grep bpmp-host + bpmp-host + ``` 2. Check that `vfio-platform` binding is successful: -``` -ghaf@ghaf-host:~]$ ls -l /sys/bus/platform/drivers/vfio-platform/3100000.serial -lrwxrwxrwx 1 root root 0 Dec 8 08:26 /sys/bus/platform/drivers/vfio-platform/3100000.serial -> ../../../../devices/platform/3100000.serial -``` + ``` + ghaf@ghaf-host:~]$ ls -l /sys/bus/platform/drivers/vfio-platform/3100000.serial + lrwxrwxrwx 1 root root 0 Dec 8 08:26 /sys/bus/platform/drivers/vfio-platform/3100000.serial -> ../../../../devices/platform/3100000.serial + ``` ### Guest for UARTA Testing +> [!TIP] > UARTA is an UART unit with a port A connection. For more information, see [UART Connections](nvidia_agx_pt_uart.md#uart-connections). -1. Build a guest kernel according to [UARTA passthrough instructions](https://github.com/jpruiz84/bpmp-virt)[^note] and use the following script to start the VM: - -> **TIP:** IMG is the kernel image and FS the rootfs. - -``` -IMG=$1 -FS=$2 - -qemu-system-aarch64 \ - -nographic \ - -machine virt,accel=kvm \ - -cpu host \ - -m 1G \ - -no-reboot \ - -kernel $IMG \ - -drive file=$FS,if=virtio,format=qcow2 \ - -net user,hostfwd=tcp::2222-:22 -net nic \ - -device vfio-platform,host=3100000.serial \ - -dtb virt.dtb \ - -append "rootwait root=/dev/vda console=ttyAMA0" -``` +1. Build a guest kernel according to [UARTA passthrough instructions](https://github.com/jpruiz84/bpmp-virt)[^note1] and use the following script to start the VM: + + > [!IMPORTANT] + > IMG is the kernel image and FS the rootfs. + + ``` + IMG=$1 + FS=$2 + + qemu-system-aarch64 \ + -nographic \ + -machine virt,accel=kvm \ + -cpu host \ + -m 1G \ + -no-reboot \ + -kernel $IMG \ + -drive file=$FS,if=virtio,format=qcow2 \ + -net user,hostfwd=tcp::2222-:22 -net nic \ + -device vfio-platform,host=3100000.serial \ + -dtb virt.dtb \ + -append "rootwait root=/dev/vda console=ttyAMA0" + ``` 2. With UARTA connected start Minicom on the working machine: -``` -minicom -b 9600 -D /dev/ttyUSB0 -``` + ``` + minicom -b 9600 -D /dev/ttyUSB0 + ``` 3. Test UARTA by echoing a string to the correct `tty` in the VM: -``` -echo 123 > /dev/ttyTHS0 -``` + ``` + echo 123 > /dev/ttyTHS0 + ``` ## Related Topics @@ -105,4 +108,4 @@ echo 123 > /dev/ttyTHS0 -[^note] That documentation is in the [bpmp-virt](https://github.com/jpruiz84/bpmp-virt) side repository, as that approach does not use microvm. +[^note1]: That documentation is in the [bpmp-virt](https://github.com/jpruiz84/bpmp-virt) side repository, as that approach does not use microvm. diff --git a/docs/src/technologies/technologies.md b/docs/src/technologies/technologies.md index 174911ed1..adfe7bd4b 100644 --- a/docs/src/technologies/technologies.md +++ b/docs/src/technologies/technologies.md @@ -36,5 +36,7 @@ In addition, we have also experimental, Aarch64 demonstrated support for a KVM v - [NVIDIA Jetson AGX Orin: UART Passthrough](./nvidia_agx_pt_uart.md) - [NVIDIA Jetson AGX Orin: PCIe Passthrough](./nvidia_agx_pt_pcie.md) - [Generic x86: PCIe Passthrough on crosvm](./x86_pcie_crosvm.md) + - [NVIDIA Jetson: UARTI Passthrough to netvm](./nvidia_uarti_net_vm.md) + - [Device Tree Overlays for Passthrough](./device_tree_overlays_pt.md) - [NVIDIA Jetson AGX Orin: Boot and Power Management Processor Virtualization](./nvidia_virtualization_bpmp.md) - [Hypervisor Options](./hypervisor_options.md) diff --git a/docs/src/technologies/x86_pcie_crosvm.md b/docs/src/technologies/x86_pcie_crosvm.md index 134a7e081..4bbfea067 100644 --- a/docs/src/technologies/x86_pcie_crosvm.md +++ b/docs/src/technologies/x86_pcie_crosvm.md @@ -11,6 +11,7 @@ As with other passthroughs, first, we need to set the target device to use VFIO driver. This can be done manually or by using the [driverctl](https://gitlab.com/driverctl/driverctl) tool as below. +> [!IMPORTANT] > Running driverctl requires root permissions. ``` diff --git a/docs/src/troubleshooting/README.md b/docs/src/troubleshooting/README.md new file mode 100644 index 000000000..90b729dd9 --- /dev/null +++ b/docs/src/troubleshooting/README.md @@ -0,0 +1,8 @@ + + +# GhafOS Troubleshooting Guide + +### 1. [systemd troubleshooting](systemd/index.md) diff --git a/docs/src/troubleshooting/systemd/early-shell.md b/docs/src/troubleshooting/systemd/early-shell.md new file mode 100644 index 000000000..636903537 --- /dev/null +++ b/docs/src/troubleshooting/systemd/early-shell.md @@ -0,0 +1,20 @@ + + +# Early shell access + +In some cases, the system may fail to boot due to the failure of a critical service. If this happens, you can follow these steps to diagnose the issue with systemd services: + +1. Increase the systemd log level using the previously mentioned option, and load the image. +2. Reboot the system. As expected, you will encounter a boot failure. +3. Force reboot the machine. When the machine starts again, interrupt the bootloader and add the following to the bootloader command line: + + ``` + rescue systemd.setenv=SYSTEMD_SULOGIN_FORCE=1 + ``` + + To modify the bootloader command, select the boot option and then press the 'e' key. + +4. You will now enter an early shell environment. Here, you can access the logs from the previous boot using `journalctl`. The logs will help you identify any service failures. diff --git a/docs/src/troubleshooting/systemd/index.md b/docs/src/troubleshooting/systemd/index.md new file mode 100644 index 000000000..fe92d295d --- /dev/null +++ b/docs/src/troubleshooting/systemd/index.md @@ -0,0 +1,14 @@ + + +# GhafOS: systemd troubleshooting guide + +Ghaf OS uses systemd and systemctl to manage services. Since security is the utmost priority, every service has restricted access to resources, which is achieved through hardened service configurations. While these restrictions enhance security, they may also limit the functionality of certain services. If a service fails, it may be necessary to adjust its configuration to restore functionality. This document focuses on troubleshooting common issues with systemd services on Ghaf OS. + +1. [Analyze system log](system-log.md) +2. [Use 'systemctl'](systemctl.md) +3. [Use systemd analyzer](systemd-analyzer.md) +4. [Use 'strace' to debug sys call and capability restrictions](strace.md) +5. [Early Shell access](early-shell.md) diff --git a/docs/src/troubleshooting/systemd/strace.md b/docs/src/troubleshooting/systemd/strace.md new file mode 100644 index 000000000..9cedb8a3a --- /dev/null +++ b/docs/src/troubleshooting/systemd/strace.md @@ -0,0 +1,26 @@ + + +# Use `strace` to debug initialization sequence + +`strace` can give detailed insight about system calls made by a service. This is very helpfull in debugging restrictions applied on system calls and capability of any service. Though we can attach `strace` with PID of a running process, but some time we may need to debug service initialization sequence also. + +To debug initialization sequence we need to attach `strace` with the service binary in `ExecStart` . To attach strace find out existing `ExecStart` of the service using command: + +```bash +$> systemctl cat .service | grep ExecStart +``` + +It will give command line options used with service binary. Now we need to override `ExecStart` of the service, in order to attach `strace`. We'll use same options with `strace`too to replicate same scenario. For example to attach `strace` with `auditd` service we'll use following configuration at a suitable location: + +```Nix +systemd.services."auditd".serviceConfig.ExecStart = lib.mkForce "${pkgs.strace}/bin/strace -o /etc/auditd_trace.log ${pkgs.audit}/bin/auditd -l -n -s nochange"; +``` + +Command`${pkgs.audit}/bin/auditd -l -n -s nochange`is used in regular `ExecStart`of `auditd`service. In above command we have attached `strace` with the command, which will generate system call traces in file `/etc/auditd_trace.log` + +After modifying above configuration you need to rebuild and load Ghaf image. + +The log may give you information about the system call restriction which caused the service failure. You can tune your service config accordingly. diff --git a/docs/src/troubleshooting/systemd/system-log.md b/docs/src/troubleshooting/systemd/system-log.md new file mode 100644 index 000000000..976c8556b --- /dev/null +++ b/docs/src/troubleshooting/systemd/system-log.md @@ -0,0 +1,74 @@ + + +# Analyze system log + +`systemd` has centralized logging mechanism which collects logs from all user processes in the system and kernel as well. This is called `journal`. systemd runs a journal daemon `journald`, which collects messages from the kernel, initrd, services, etc. + +Analyzing logs is the most effective way to diagnose issues with any systemd service. In Ghaf OS, the default systemd log level is set to `info`. To gain deeper insights into the service state, the log level can be elevated to `debug`using the following option: + +``` +ghaf.systemd.logLevel = "debug"; +``` + +While it is possible to elevate the log level on a live system using `systemctl`, this option is particularly useful when you need to inspect the startup sequence of critical services that cannot be restarted in a live environment. + +To change the log level to `debug`, you can run the following `systemctl` command: + +```bash +$> sudo systemctl log-level debug +``` + +This command will change the log level for the systemd daemon and all systemd-managed services. + +After adjusting the log level, it is recommended to reload the systemd daemon and restart the service you are debugging. + +## `journalctl` + +When `journalctl` command is run without any option, it will show all the messages, which can be pretty long. + +1. You can see logs of specific boot using -b option for example: + +```bash +$> journalctl -b #Log from current boot +$> journalctl -b -1 #Log from previous boo +``` + +2. To list available boots, use the following command. + +``` +$> journalctl --list-boots +``` + +3. To view the logs generated by any systemd unit, use the `-u` option. For example, the command below displays all logs recorded by the logind service. You can specify multiple units by using the -u` switch more than once. + +```bash +$> journalctl -u logind.service +``` + +4. `You can see log messages in real-time, similar to the `tail`command in Linux. To do this, use the`-f` option: + +```bash +$> journalctl -f +``` + +5. Similar to the `tail`command, the`-n` option allows you to display a specific number of the most recent log entries. The following command shows the last 50 messages logged. + +```bash +$> journalctl -n 50 +``` + +6. Log messages can be filtered based on their priority using -p option, for example follwing command will show only error message from service logind + + ```bash + $> journalctl -p error -u logind.service + ``` +7. To see kernel message use following options: + + ```bash + $> journalctl -k + $> journalctl -t kernel + ``` +8. The `-r` option displays log entries in reverse chronological order, with the latest messages shown first. diff --git a/docs/src/troubleshooting/systemd/systemctl.md b/docs/src/troubleshooting/systemd/systemctl.md new file mode 100644 index 000000000..b88234c8f --- /dev/null +++ b/docs/src/troubleshooting/systemd/systemctl.md @@ -0,0 +1,79 @@ + + +# Debuuging systemd using`systemctl` + +To debug failed services using `systemctl` follow below given steps: + +1) List failed services in the system: + + ```bash + $> sudo systemctl --failed + ``` + + Above command will give you list of failed services. You can see list of all the services in the system using the command: + + ``` + $> sudo systemctl list-unit-files --type=service + ``` + +2. Check status of the failed service, it will you give little more detailed information. + + ```bash + $> sudo systemctl status .service + ``` +3. See the service logs to get more insight: + + ``` + $> sudo journalctl -b -u .service + ``` +4. You can further increase log level to get debug level information: + + ```bash + $> sudo systemctl log-level debug + ``` + + Reload the systemd daemon and restart service: + + ```bash + $> sudo systemctl daemon-reload + $> sudo systemctl restart .service + ``` + + Now you can see debug level information in the service log. +5. You can also attach `strace` with the service daemon to see system call and signal status. + + - Get the PID of main process from service status. It is listed as `Main PID:` + - Attach strace with the PID: + + ```bash + $> sudo strace -f -s 100 -p + ``` +6. Retune the service configuration in runtime: + + ```bash + $> systemctl edit --runtime .service + ``` + + - Uncomment the `[Service]`section and also uncomment the configuration you want to enable or disable. You can add any new configuration. This basically overrides your base configuration. + - Save the configuration as `/run/systemd/system/.d/override.conf` + - Reload the systemd daemon and restart the service as mentioned in step 4. + - You can check if your service is using the new configuration using command: + + ``` + $> sudo systemctl show .service + ``` + - You see base configuration also: + + ```bash + $> sudo systemctl cat .service + ``` +7. If the new configuration works for you, you can check the exposure level of the service using command: + + ```bash + $> systemd-analyze security + $> systemd-analyze security .service #For detailed information + ``` +8. Update the configuration in Ghaf repo and build it. Hardened service configs are available in directory `ghaf/modules/common/systemd/hardened-configs` diff --git a/docs/src/troubleshooting/systemd/systemd-analyzer.md b/docs/src/troubleshooting/systemd/systemd-analyzer.md new file mode 100644 index 000000000..ee1c267f7 --- /dev/null +++ b/docs/src/troubleshooting/systemd/systemd-analyzer.md @@ -0,0 +1,88 @@ + + +# `systemd-analyze` Tool + +`systemd-analyze` is a powerful tool that helps diagnose and troubleshoot issues related to systemd services. It provides various commands to analyze the performance and dependencies of services, as well as to pinpoint issues during the boot process. + +### Steps to Analyze Systemd Services + +#### 1. **Analyze Boot Performance** + +`systemd-analyze` can help you understand how long each service takes to start during boot. This is useful for identifying services that are slowing down the boot process. + + +* To get a summary of the boot time: + + ```bash + $> systemd-analyze + ``` + + This command shows the overall time taken to boot, including the kernel, initrd, and userspace times. +* To see a detailed breakdown of how long each service took to start: + + ```bash + $> systemd-analyze blame + ``` + + This lists all services in order of their startup time, with the slowest services listed first. +* For a graphical representation of the boot process, you can use: + + ```bash + $> system-analyze plot > boot-time.svg + ``` + + This command generates an SVG file that visually represents the startup times of all services. You can view this file in any web browser. + +#### 2. View Service Dependencies + +To troubleshoot issues related to service dependencies, you can visualize the dependency tree of a specific service. To display the dependency tree of a service: + +```bash +systemd-analyze critical-chain .service +``` + +This command shows the critical path that affects the startup time of the service, highlighting any dependencies that may delay its startup. + + +#### 3. Verify Unit Files + +To verify the configuration of a service's unit file: + +- ```bash + $> systemd-analyze verify .service + ``` + + This command checks the syntax and can help identify configuration issues. + +#### 4. Check for Cyclic Dependencies + +Cyclic dependencies can cause services to fail or hang during boot. systemd-analyze can check for these issues: + +- To check for any cyclic dependencies: + + ``` + $> systemd-analyze verify --man=your-service-name.service + ``` + + This will warn you about any loops or issues within the unit's dependency tree. + + + +#### 5. Analyze Security Settings + +`systemd-analyze` can also assess the security of your service’s configuration: + +- To evaluate the overall threat exposure of systemd services, use: + + ```bash + $> systemd-analyze security + ``` +- To evaluate the security of a specific service: + + ```bash + $> systemd-analyze security .service + ``` + This command provides a security assessment, scoring the service based on various hardening options and highlighting potential weaknesses. diff --git a/docs/style_guide.md b/docs/style_guide.md index 824a7a5bc..7475f95ab 100644 --- a/docs/style_guide.md +++ b/docs/style_guide.md @@ -99,27 +99,41 @@ To make our Markdown files maintainable over time and across teams, follow the r * Notes with quoting - Use an angle bracket (>) for annotations. For example: - ``` - > This is a note. - ``` - To draw more attention, you can create note blocks simply by surrounding the content with two horizontal lines. For example: + Use an angle bracket (>) for annotations. + + Our mdBook is extended with the [mdbook-alerts](https://github.com/lambdalisue/rs-mdbook-alerts) third-party plugin[^note1] which adds usage of [GitHub Flavored Markdown's Alerts](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts). To emphasize critical information, please use the following syntax: + ``` - --- - **IMPORTANT** + > [!NOTE] + > Useful information that users should know, even when skimming content. + + > [!TIP] + > Helpful advice for doing things better or more easily. - Very importamt information. + > [!IMPORTANT] + > Key information users need to know to achieve their goal. - --- + > [!WARNING] + > Urgent info that needs immediate user attention to avoid problems. + + > [!CAUTION] + > Advises about risks or negative outcomes of certain actions. ``` +* Footnotes + + For footnote references, use an identifier inside brackets. For example: + in a text: `... this template[^note1] ...` + at the end of the file: `[^note1]: This template is based on...` + * Markdown shields (badges) In [README.md](../README.md) and [README-docs.md](./README-docs.md), we used those emblems so that the user can see the needed information at first glance. In fact, it is just a reference link. To make your own shield, use [shields.io](https://shields.io/). * Unicode characters - For GitHub .md files (not for GitHub Pages), emojis are welcome :octocat:. [Supported GitHub emojis](https://github-emoji-picker.vercel.app/). + For GitHub .md files (not for GitHub Pages), emojis are welcome :octocat:. + Check [Supported GitHub emojis](https://github-emoji-picker.vercel.app/) for more inspiration. @@ -222,3 +236,6 @@ Congratulations! You found the Room of Requirement that adjusts itself to its se Happy writing! + + +[^note1]: For the full list of available community-developed plugins for extending mdBook, see [Third party plugins](https://github.com/rust-lang/mdBook/wiki/Third-party-plugins). diff --git a/docs/theme/index.hbs b/docs/theme/index.hbs new file mode 100644 index 000000000..420e89c83 --- /dev/null +++ b/docs/theme/index.hbs @@ -0,0 +1,478 @@ + + + + + + + {{ title }} + {{#if is_print }} + + {{/if}} {{#if base_url}} + + {{/if}} + + + {{> head}} + + + + + + {{#if favicon_svg}} + + {{/if}} {{#if favicon_png}} + + {{/if}} + + + + {{#if print_enable}} + + {{/if}} + + + + {{#if copy_fonts}} + + {{/if}} + + + + + + + + {{#each additional_css}} + + {{/each}} {{#if mathjax_support}} + + + {{/if}} + + +
+ + + + + + + + + + + + + + + + + + + +
+
+ {{> header}} + + + + {{#if search_enabled}} + + {{/if}} + + + + +
+
+
{{{ content }}}
+
+ +
+
+ + +
+
+ + +
+ + {{#if live_reload_endpoint}} + + + {{/if}} {{#if google_analytics}} + + + {{/if}} {{#if playground_line_numbers}} + + {{/if}} {{#if playground_copyable}} + + {{/if}} {{#if playground_js}} + + + + + + {{/if}} {{#if search_js}} + + + + {{/if}} + + + + + + + {{#each additional_js}} + + {{/each}} {{#if is_print}} {{#if mathjax_support}} + + {{else}} + + {{/if}} {{/if}} +
+ + diff --git a/docs/theme/pagetoc.css b/docs/theme/pagetoc.css new file mode 100644 index 000000000..40fc81fbd --- /dev/null +++ b/docs/theme/pagetoc.css @@ -0,0 +1,107 @@ +/* +Copyright 2020 Jorel Ali +Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +SPDX-License-Identifier: WTFPL +*/ + +:root { + --toc-width: 270px; + --center-content-toc-shift: calc(-1 * var(--toc-width) / 2); +} + +.nav-chapters { + /* adjust width of buttons that bring to the previous or the next page */ + min-width: 50px; +} + +.previous { + /* + adjust the space between the left sidebar or the left side of the screen + and the button that leads to the previous page + */ + margin-left: var(--page-padding); +} + +@media only screen { + main { + display: flex; + } + + @media (max-width: 1179px) { + .sidebar-hidden .sidetoc { + display: none; + } + } + + @media (max-width: 1439px) { + .sidebar-visible .sidetoc { + display: none; + } + } + + @media (1180px <= width <= 1439px) { + .sidebar-hidden main { + position: relative; + left: var(--center-content-toc-shift); + } + } + + @media (1440px <= width <= 1700px) { + .sidebar-visible main { + position: relative; + left: var(--center-content-toc-shift); + } + } + + .content-wrap { + overflow-y: auto; + width: 100%; + } + + .sidetoc { + margin-top: 20px; + margin-left: 10px; + margin-right: auto; + } + .pagetoc { + position: fixed; + /* adjust TOC width */ + width: var(--toc-width); + height: calc(100vh - var(--menu-bar-height) - 0.67em * 4); + overflow: auto; + } + .pagetoc a { + border-left: 1px solid var(--sidebar-bg); + color: var(--fg) !important; + display: block; + padding-bottom: 5px; + padding-top: 5px; + padding-left: 10px; + text-align: left; + text-decoration: none; + } + .pagetoc a:hover, + .pagetoc a.active { + background: var(--sidebar-bg); + color: var(--sidebar-fg) !important; + } + .pagetoc .active { + background: var(--sidebar-bg); + color: var(--sidebar-fg); + } + .pagetoc .pagetoc-H2 { + padding-left: 20px; + } + .pagetoc .pagetoc-H3 { + padding-left: 40px; + } + .pagetoc .pagetoc-H4 { + padding-left: 60px; + } +} + +@media print { + .sidetoc { + display: none; + } +} diff --git a/docs/theme/pagetoc.js b/docs/theme/pagetoc.js new file mode 100644 index 000000000..9fdd878b2 --- /dev/null +++ b/docs/theme/pagetoc.js @@ -0,0 +1,120 @@ +/* +Copyright 2020 Jorel Ali +Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +SPDX-License-Identifier: WTFPL +*/ + +function forEach(elems, fun) { + Array.prototype.forEach.call(elems, fun); +} + +function getPagetoc() { + return document.getElementsByClassName("pagetoc")[0]; +} + +function getPagetocElems() { + return getPagetoc().children; +} + +function getHeaders() { + return document.getElementsByClassName("header"); +} + +// Un-active everything when you click it +function forPagetocElem(fun) { + forEach(getPagetocElems(), fun); +} + +function getRect(element) { + return element.getBoundingClientRect(); +} + +function overflowTop(container, element) { + return getRect(container).top - getRect(element).top; +} + +function overflowBottom(container, element) { + return getRect(container).bottom - getRect(element).bottom; +} + +var activeHref = location.href; + +var updateFunction = function (elem = undefined) { + var id = elem; + + if (!id && location.href != activeHref) { + activeHref = location.href; + forPagetocElem(function (el) { + if (el.href === activeHref) { + id = el; + } + }); + } + + if (!id) { + var elements = getHeaders(); + let margin = window.innerHeight / 3; + + forEach(elements, function (el, i, arr) { + if (!id && getRect(el).top >= 0) { + if (getRect(el).top < margin) { + id = el; + } else { + id = arr[Math.max(0, i - 1)]; + } + } + // a very long last section + // its heading is over the screen + if (!id && i == arr.length - 1) { + id = el; + } + }); + } + + forPagetocElem(function (el) { + el.classList.remove("active"); + }); + + if (!id) return; + + forPagetocElem(function (el) { + if (id.href.localeCompare(el.href) == 0) { + el.classList.add("active"); + let pagetoc = getPagetoc(); + if (overflowTop(pagetoc, el) > 0) { + pagetoc.scrollTop = el.offsetTop; + } + if (overflowBottom(pagetoc, el) < 0) { + pagetoc.scrollTop -= overflowBottom(pagetoc, el); + } + } + }); +}; + +let elements = getHeaders(); + +if (elements.length > 1) { + // Populate sidebar on load + window.addEventListener("load", function () { + var pagetoc = getPagetoc(); + var elements = getHeaders(); + forEach(elements, function (el) { + var link = document.createElement("a"); + link.appendChild(document.createTextNode(el.text)); + link.href = el.hash; + link.classList.add("pagetoc-" + el.parentElement.tagName); + pagetoc.appendChild(link); + link.onclick = function () { + updateFunction(link); + }; + }); + updateFunction(); + }); + + // Handle active elements on scroll + window.addEventListener("scroll", function () { + updateFunction(); + }); +} else { + document.getElementsByClassName("sidetoc")[0].remove(); +} diff --git a/flake.lock b/flake.lock index 76569b6c8..ed7792cee 100644 --- a/flake.lock +++ b/flake.lock @@ -2,29 +2,38 @@ "nodes": { "crane": { "inputs": { - "flake-compat": [ - "lanzaboote", - "flake-compat" - ], - "flake-utils": [ - "lanzaboote", - "flake-utils" - ], "nixpkgs": [ - "lanzaboote", + "givc", "nixpkgs" - ], - "rust-overlay": [ + ] + }, + "locked": { + "lastModified": 1720975002, + "narHash": "sha256-1i521ecK2MFg+lxSk9oRx/C0SsdlI6GS6eYT79nA6TA=", + "owner": "ipetkov", + "repo": "crane", + "rev": "1791a5b98d2c1bf143ad85469abcfa2426f3f087", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "crane_2": { + "inputs": { + "nixpkgs": [ "lanzaboote", - "rust-overlay" + "nixpkgs" ] }, "locked": { - "lastModified": 1681177078, - "narHash": "sha256-ZNIjBDou2GOabcpctiQykEQVkI8BDwk7TyvlWlI4myE=", + "lastModified": 1717535930, + "narHash": "sha256-1hZ/txnbd/RmiBPNUs7i8UQw2N89uAK3UzrGAWdnFfU=", "owner": "ipetkov", "repo": "crane", - "rev": "0c9f468ff00576577d83f5019a66c557ede5acf6", + "rev": "55e7754ec31dac78980c8be45f8a28e80e370946", "type": "github" }, "original": { @@ -35,17 +44,16 @@ }, "devshell": { "inputs": { - "flake-utils": "flake-utils", "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1705332421, - "narHash": "sha256-USpGLPme1IuqG78JNqSaRabilwkCyHmVWY0M9vYyqEA=", + "lastModified": 1722113426, + "narHash": "sha256-Yo/3loq572A8Su6aY5GP56knpuKYRvM2a1meP9oJZCw=", "owner": "numtide", "repo": "devshell", - "rev": "83cb93d6d063ad290beee669f4badf9914cc16ec", + "rev": "67cce7359e4cd3c45296fb4aaf6a19e2a9c757ae", "type": "github" }, "original": { @@ -61,28 +69,49 @@ ] }, "locked": { - "lastModified": 1712612224, - "narHash": "sha256-Tv4C8OSPVmm4LbpJGLFSODyvJy6DqrisEGPCQdNVOeY=", + "lastModified": 1723080788, + "narHash": "sha256-C5LbM5VMdcolt9zHeLQ0bYMRjUL+N+AL5pK7/tVTdes=", "owner": "nix-community", "repo": "disko", - "rev": "79eab0e82cb126bf4ac170f44af82479f0895ab5", + "rev": "ffc1f95f6c28e1c6d1e587b51a2147027a3e45ed", "type": "github" }, "original": { "owner": "nix-community", - "ref": "master", "repo": "disko", "type": "github" } }, + "fenix": { + "inputs": { + "nixpkgs": [ + "microvm", + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1722580276, + "narHash": "sha256-VaNcSh7n8OaFW/DJsR6Fm23V+EGpSei0DyF71RKB+90=", + "owner": "nix-community", + "repo": "fenix", + "rev": "286f371b3cfeaa5c856c8e6dfb893018e86cc947", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, "flake-compat": { "flake": false, "locked": { - "lastModified": 1688025799, - "narHash": "sha256-ktpB4dRtnksm9F5WawoIkEneh1nrEvuxb5lJFt1iOyw=", + "lastModified": 1717312683, + "narHash": "sha256-FrlieJH50AuvagamEvWMIE6D2OAnERuDboFDYAED/dE=", "owner": "nix-community", "repo": "flake-compat", - "rev": "8bf105319d44f6b9f0d764efa4fdef9f1cc9ba1c", + "rev": "38fd3954cf65ce6faf3d0d45cd26059e059f07ea", "type": "github" }, "original": { @@ -98,11 +127,11 @@ ] }, "locked": { - "lastModified": 1706830856, - "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "lastModified": 1722555600, + "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", + "rev": "8471fe90ad337a8074e957b69ca4d0089218391d", "type": "github" }, "original": { @@ -113,11 +142,11 @@ }, "flake-root": { "locked": { - "lastModified": 1692742795, - "narHash": "sha256-f+Y0YhVCIJ06LemO+3Xx00lIcqQxSKJHXT/yk1RTKxw=", + "lastModified": 1713493429, + "narHash": "sha256-ztz8JQkI08tjKnsTpfLqzWoKFQF4JGu2LRz8bkdnYUk=", "owner": "srid", "repo": "flake-root", - "rev": "d9a70d9c7a5fd7f3258ccf48da9335e9b47c3937", + "rev": "bc748b93b86ee76e2032eecda33440ceb2532fcd", "type": "github" }, "original": { @@ -126,25 +155,22 @@ "type": "github" } }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, + "flake-root_2": { "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "lastModified": 1723604017, + "narHash": "sha256-rBtQ8gg+Dn4Sx/s+pvjdq3CB2wQNzx9XGFq/JVGCB6k=", + "owner": "srid", + "repo": "flake-root", + "rev": "b759a56851e10cb13f6b8e5698af7b59c44be26e", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "srid", + "repo": "flake-root", "type": "github" } }, - "flake-utils_2": { + "flake-utils": { "inputs": { "systems": [ "systems" @@ -164,6 +190,39 @@ "type": "github" } }, + "ghafpkgs": { + "inputs": { + "flake-compat": [ + "flake-compat" + ], + "flake-parts": [ + "flake-parts" + ], + "flake-root": "flake-root_2", + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks-nix": [ + "pre-commit-hooks-nix" + ], + "treefmt-nix": [ + "treefmt-nix" + ] + }, + "locked": { + "lastModified": 1724328564, + "narHash": "sha256-dx3lpB8YRPtbFSKNAx2ngYXrfPciYxLNBzhnssdWoxg=", + "owner": "tiiuae", + "repo": "ghafpkgs", + "rev": "e41e2306eed51f0bdb915b2120006466456a87db", + "type": "github" + }, + "original": { + "owner": "tiiuae", + "repo": "ghafpkgs", + "type": "github" + } + }, "gitignore": { "inputs": { "nixpkgs": [ @@ -185,6 +244,57 @@ "type": "github" } }, + "givc": { + "inputs": { + "crane": "crane", + "devshell": [ + "devshell" + ], + "flake-parts": [ + "flake-parts" + ], + "flake-root": [ + "flake-root" + ], + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks-nix": [ + "pre-commit-hooks-nix" + ], + "treefmt-nix": [ + "treefmt-nix" + ] + }, + "locked": { + "lastModified": 1724752104, + "narHash": "sha256-wNYN6dSsLPGLWkrb27/YswsE0kqkw29m6UCbMBDR600=", + "owner": "tiiuae", + "repo": "ghaf-givc", + "rev": "ff9f60e3059f940fad610c27393b4d101bf6693d", + "type": "github" + }, + "original": { + "owner": "tiiuae", + "repo": "ghaf-givc", + "type": "github" + } + }, + "impermanence": { + "locked": { + "lastModified": 1717932370, + "narHash": "sha256-7C5lCpiWiyPoIACOcu2mukn/1JRtz6HC/1aEMhUdcw0=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "27979f1c3a0d3b9617a3563e2839114ba7d48d3f", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "impermanence", + "type": "github" + } + }, "jetpack-nixos": { "inputs": { "nixpkgs": [ @@ -192,22 +302,23 @@ ] }, "locked": { - "lastModified": 1707323143, - "narHash": "sha256-Mfj2l2aE+3Vu/u1M1PtQTvIoOZfCkINgtCQagSZFU6Q=", + "lastModified": 1718600161, + "narHash": "sha256-f51gOZCrmNNOFTyfYxPSDxYZPALGydjWsSPVz6skY0o=", "owner": "anduril", "repo": "jetpack-nixos", - "rev": "6ae4ce1d368fb56235a8b15ef926db28c4643eb8", + "rev": "793716c1ca29a1be6d9bea84296a933c4acdddc1", "type": "github" }, "original": { "owner": "anduril", "repo": "jetpack-nixos", + "rev": "793716c1ca29a1be6d9bea84296a933c4acdddc1", "type": "github" } }, "lanzaboote": { "inputs": { - "crane": "crane", + "crane": "crane_2", "flake-compat": [ "flake-compat" ], @@ -226,22 +337,23 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1682802423, - "narHash": "sha256-Fb5TeRTdvUlo/5Yi2d+FC8a6KoRLk2h1VE0/peMhWPs=", + "lastModified": 1718178907, + "narHash": "sha256-eSZyrQ9uoPB9iPQ8Y5H7gAmAgAvCw3InStmU3oEjqsE=", "owner": "nix-community", "repo": "lanzaboote", - "rev": "64b903ca87d18cef2752c19c098af275c6e51d63", + "rev": "b627ccd97d0159214cee5c7db1412b75e4be6086", "type": "github" }, "original": { "owner": "nix-community", - "ref": "v0.3.0", + "ref": "v0.4.1", "repo": "lanzaboote", "type": "github" } }, "microvm": { "inputs": { + "fenix": "fenix", "flake-utils": [ "flake-utils" ], @@ -251,11 +363,11 @@ "spectrum": "spectrum" }, "locked": { - "lastModified": 1707953231, - "narHash": "sha256-xdhJQH4ER3lqaNJ+ZxhmNhjFn47HIsWdSpzWhl6dRAY=", + "lastModified": 1723407630, + "narHash": "sha256-iBvdy5KAYWew4sAIVbrqrNL7jCMWFoB5hObocCXkHiY=", "owner": "astro", "repo": "microvm.nix", - "rev": "d350318cb7f40a300b4b4674acf9bee26933ecca", + "rev": "802ef1704f6a050f272bed5e226d0e86fa3e8c39", "type": "github" }, "original": { @@ -277,11 +389,11 @@ ] }, "locked": { - "lastModified": 1703607026, - "narHash": "sha256-Emh0BPoqlS4ntp2UJrwydXfIP4qIMF0VBB2FUE3/M/E=", + "lastModified": 1719475157, + "narHash": "sha256-8zW6eWvE9T03cMpo/hY8RRZIsSCfs1zmsJOkEZzuYwM=", "owner": "Mic92", "repo": "nix-fast-build", - "rev": "4376b8a33b217ee2f78ba3dcff01a3e464d13a46", + "rev": "030e586195c97424844965d2ce680140f6565c02", "type": "github" }, "original": { @@ -292,11 +404,11 @@ }, "nixlib": { "locked": { - "lastModified": 1693701915, - "narHash": "sha256-waHPLdDYUOHSEtMKKabcKIMhlUOHPOOPQ9UyFeEoovs=", + "lastModified": 1722732880, + "narHash": "sha256-do2Mfm3T6SR7a5A804RhjQ+JTsF5hk4JTPGjCTRM/m8=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "f5af57d3ef9947a70ac86e42695231ac1ad00c25", + "rev": "8bebd4c74f368aacb047f0141db09ec6b339733c", "type": "github" }, "original": { @@ -313,11 +425,11 @@ ] }, "locked": { - "lastModified": 1707873059, - "narHash": "sha256-simzllUEmzVqmQogcGCorfIbJpodAhgGSr6vuFtd4XQ=", + "lastModified": 1723078345, + "narHash": "sha256-HSxOQEKNZXiJe9aWnckTTCThOhcRCabwHa32IduDKLk=", "owner": "nix-community", "repo": "nixos-generators", - "rev": "0aa24e93f75370454f0e03747b6836ac2a2c9fca", + "rev": "d6c5d29f58acc10ea82afff1de2b28f038f572bd", "type": "github" }, "original": { @@ -328,11 +440,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1707842204, - "narHash": "sha256-M+HAq1qWQBi/gywaMZwX0odU+Qb/XeqVeANGKRBDOwU=", + "lastModified": 1723149858, + "narHash": "sha256-3u51s7jdhavmEL1ggtd8wqrTH2clTy5yaZmhLvAXTqc=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "f1b2f71c86a5b1941d20608db0b1e88a07d31303", + "rev": "107bb46eef1f05e86fc485ee8af9b637e5157988", "type": "github" }, "original": { @@ -343,16 +455,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1707786466, - "narHash": "sha256-yLPfrmW87M2qt+8bAmwopJawa+MJLh3M9rUbXtpUc1o=", - "owner": "NixOS", + "lastModified": 1722593986, + "narHash": "sha256-JeH0R7hlzEWCFkQLnUxFm8hfcQMsXzdl4jCIejU6vzs=", + "owner": "tiiuae", "repo": "nixpkgs", - "rev": "01885a071465e223f8f68971f864b15829988504", + "rev": "c488d21b64527c6f4fb4a6ce686112b238791ec6", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixos-23.11", + "owner": "tiiuae", + "ref": "nixos-unstable-texinfo", "repo": "nixpkgs", "type": "github" } @@ -362,9 +474,6 @@ "flake-compat": [ "flake-compat" ], - "flake-utils": [ - "flake-utils" - ], "gitignore": "gitignore", "nixpkgs": [ "nixpkgs" @@ -374,11 +483,11 @@ ] }, "locked": { - "lastModified": 1707297608, - "narHash": "sha256-ADjo/5VySGlvtCW3qR+vdFF4xM9kJFlRDqcC9ZGI8EA=", + "lastModified": 1723056346, + "narHash": "sha256-YpzywjTAUHRRHcO8zz9x2gYqJ0JmZlcB9+RaUvD89qM=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "0db2e67ee49910adfa13010e7f012149660af7f0", + "rev": "3c977f1c9930f54066c085305b4b2291385e7a73", "type": "github" }, "original": { @@ -394,7 +503,10 @@ "flake-compat": "flake-compat", "flake-parts": "flake-parts", "flake-root": "flake-root", - "flake-utils": "flake-utils_2", + "flake-utils": "flake-utils", + "ghafpkgs": "ghafpkgs", + "givc": "givc", + "impermanence": "impermanence", "jetpack-nixos": "jetpack-nixos", "lanzaboote": "lanzaboote", "microvm": "microvm", @@ -403,10 +515,27 @@ "nixos-hardware": "nixos-hardware", "nixpkgs": "nixpkgs", "pre-commit-hooks-nix": "pre-commit-hooks-nix", - "systems": "systems_2", + "systems": "systems", "treefmt-nix": "treefmt-nix" } }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1722521768, + "narHash": "sha256-FvJ4FaMy1kJbZ3Iw1RyvuiUAsbHJXoU2HwylzaFzj1o=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "f149dc5029d8406fae8b2c541603bcac06e30deb", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, "rust-overlay": { "inputs": { "flake-utils": [ @@ -419,11 +548,11 @@ ] }, "locked": { - "lastModified": 1682129965, - "narHash": "sha256-1KRPIorEL6pLpJR04FwAqqnt4Tzcm4MqD84yhlD+XSk=", + "lastModified": 1717813066, + "narHash": "sha256-wqbRwq3i7g5EHIui0bIi84mdqZ/It1AXBSLJ5tafD28=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "2c417c0460b788328220120c698630947547ee83", + "rev": "6dc3e45fe4aee36efeed24d64fc68b1f989d5465", "type": "github" }, "original": { @@ -435,11 +564,11 @@ "spectrum": { "flake": false, "locked": { - "lastModified": 1703273931, - "narHash": "sha256-CJ1Crdi5fXHkCiemovsp20/RC4vpDaZl1R6V273FecI=", + "lastModified": 1720264467, + "narHash": "sha256-xzM92n3Q9L90faJIJrkrTtTx+JqCGRHMkHWztkV4PuY=", "ref": "refs/heads/main", - "rev": "97e2f3429ee61dc37664b4d096b2fec48a57b691", - "revCount": 597, + "rev": "fb59d42542049f586c84b0f8bb86ff3be338e9d3", + "revCount": 674, "type": "git", "url": "https://spectrum-os.org/git/spectrum" }, @@ -463,21 +592,6 @@ "type": "github" } }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, "treefmt-nix": { "inputs": { "nixpkgs": [ @@ -485,11 +599,11 @@ ] }, "locked": { - "lastModified": 1707300477, - "narHash": "sha256-qQF0fEkHlnxHcrKIMRzOETnRBksUK048MXkX0SOmxvA=", + "lastModified": 1722330636, + "narHash": "sha256-uru7JzOa33YlSRwf9sfXpJG+UAV+bnBEYMjrzKrQZFw=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "ac599dab59a66304eb511af07b3883114f061b9d", + "rev": "768acdb06968e53aa1ee8de207fd955335c754b7", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 1d1d73605..8d005ef24 100644 --- a/flake.nix +++ b/flake.nix @@ -1,22 +1,25 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 { - description = "Ghaf - Documentation and implementation for TII SSRC Secure Technologies Ghaf Framework"; + description = "Ghaf Framework: Documentation and implementation for TII SSRC Secure Technologies"; nixConfig = { substituters = [ + "https://dev-cache.vedenemo.dev" "https://cache.vedenemo.dev" "https://cache.ssrcdevops.tii.ae" "https://ghaf-dev.cachix.org" "https://cache.nixos.org/" ]; extra-trusted-substituters = [ + "https://dev-cache.vedenemo.dev" "https://cache.vedenemo.dev" "https://cache.ssrcdevops.tii.ae" "https://ghaf-dev.cachix.org" "https://cache.nixos.org/" ]; extra-trusted-public-keys = [ + "ghaf-infra-dev:EdgcUJsErufZitluMOYmoJDMQE+HFyveI/D270Cr84I=" "cache.vedenemo.dev:8NhplARANhClUSWJyLVk4WMyy1Wb4rhmWW2u8AejH9E=" "cache.ssrcdevops.tii.ae:oOrzj9iCppf+me5/3sN/BxEkp5SaFkHfKTPPZ97xXQk=" "ghaf-dev.cachix.org-1:S3M8x3no8LFQPBfHw1jl6nmP8A7cVWKntoMKN3IsEQY=" @@ -25,7 +28,20 @@ }; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11"; + #TODO: clean this up before merging to main + nixpkgs.url = "github:tiiuae/nixpkgs/nixos-unstable-texinfo"; # "flake:mylocalnixpkgs"; # + #nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + ghafpkgs = { + url = "github:tiiuae/ghafpkgs"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-parts.follows = "flake-parts"; + treefmt-nix.follows = "treefmt-nix"; + pre-commit-hooks-nix.follows = "pre-commit-hooks-nix"; + flake-compat.follows = "flake-compat"; + }; + }; # # Flake and repo structuring configurations @@ -50,7 +66,6 @@ inputs = { nixpkgs.follows = "nixpkgs"; nixpkgs-stable.follows = "nixpkgs"; - flake-utils.follows = "flake-utils"; flake-compat.follows = "flake-compat"; }; }; @@ -102,12 +117,12 @@ nixos-hardware.url = "github:NixOS/nixos-hardware"; jetpack-nixos = { - url = "github:anduril/jetpack-nixos"; + url = "github:anduril/jetpack-nixos/793716c1ca29a1be6d9bea84296a933c4acdddc1"; inputs.nixpkgs.follows = "nixpkgs"; }; disko = { - url = "github:nix-community/disko/master"; + url = "github:nix-community/disko"; inputs.nixpkgs.follows = "nixpkgs"; }; @@ -115,7 +130,7 @@ # Security # lanzaboote = { - url = "github:nix-community/lanzaboote/v0.3.0"; + url = "github:nix-community/lanzaboote/v0.4.1"; inputs = { nixpkgs.follows = "nixpkgs"; flake-utils.follows = "flake-utils"; @@ -124,15 +139,30 @@ flake-compat.follows = "flake-compat"; }; }; + + impermanence = { + url = "github:nix-community/impermanence"; + }; + + givc = { + url = "github:tiiuae/ghaf-givc"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-parts.follows = "flake-parts"; + flake-root.follows = "flake-root"; + treefmt-nix.follows = "treefmt-nix"; + devshell.follows = "devshell"; + pre-commit-hooks-nix.follows = "pre-commit-hooks-nix"; + }; + }; }; - outputs = inputs @ {flake-parts, ...}: let - lib = import ./lib.nix {inherit inputs;}; - in - flake-parts.lib.mkFlake - { - inherit inputs; - } { + outputs = + inputs@{ flake-parts, ... }: + let + lib = import ./lib.nix { inherit inputs; }; + in + flake-parts.lib.mkFlake { inherit inputs; } { # Toggle this to allow debugging in the repl # see:https://flake.parts/debug debug = false; @@ -156,11 +186,6 @@ ./templates/flake-module.nix ]; - #TODO Fix this - #flake.nixosModules = with lib; - # mapAttrs (_: import) - # (flattenTree (rakeLeaves ./modules)); - flake.lib = lib; }; } diff --git a/hydrajobs/flake-module.nix b/hydrajobs/flake-module.nix index 3d76a62ae..a7c7fa1ea 100644 --- a/hydrajobs/flake-module.nix +++ b/hydrajobs/flake-module.nix @@ -1,37 +1,48 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{self, ...}: let - mkBpmpEnabled = cfg: let - bpmpEnableModule = {lib, ...}: { - ghaf.hardware.nvidia = { - virtualization.enable = lib.mkForce true; - virtualization.host.bpmp.enable = lib.mkForce true; - passthroughs.host.uarta.enable = lib.mkForce true; - }; - }; - newCfg = cfg.extendModules {modules = [bpmpEnableModule];}; - package = newCfg.config.system.build.${newCfg.config.formatAttr}; - in +{ self, ... }: +let + mkBpmpEnabled = + cfg: + let + bpmpEnableModule = + { lib, ... }: + { + ghaf.hardware.nvidia = { + virtualization.enable = lib.mkForce true; + virtualization.host.bpmp.enable = lib.mkForce true; + passthroughs.host.uarta.enable = lib.mkForce true; + }; + }; + newCfg = cfg.extendModules { modules = [ bpmpEnableModule ]; }; + package = newCfg.config.system.build.${newCfg.config.formatAttr}; + in package; -in { +in +{ flake.hydraJobs = { generic-x86_64-debug.x86_64-linux = self.packages.x86_64-linux.generic-x86_64-debug; lenovo-x1-carbon-gen11-debug.x86_64-linux = self.packages.x86_64-linux.lenovo-x1-carbon-gen11-debug; - nvidia-jetson-orin-agx-debug.aarch64-linux = self.packages.aarch64-linux.nvidia-jetson-orin-agx-debug; + nvidia-jetson-orin-agx-debug.aarch64-linux = + self.packages.aarch64-linux.nvidia-jetson-orin-agx-debug; nvidia-jetson-orin-nx-debug.aarch64-linux = self.packages.aarch64-linux.nvidia-jetson-orin-nx-debug; intel-vm-debug.x86_64-linux = self.packages.x86_64-linux.vm-debug; - imx8qm-mek-debug.aarch64-linux = self.packages.aarch64-linux.imx8qm-mek-debug; + nxp-imx8mp-evk-debug.x86_64-linux = self.packages.aarch64-linux.nxp-imx8mp-evk-debug; docs.x86_64-linux = self.packages.x86_64-linux.doc; docs.aarch64-linux = self.packages.aarch64-linux.doc; - microchip-icicle-kit-debug.x86_64-linux = self.packages.riscv64-linux.microchip-icicle-kit-debug; - - # Build cross-copmiled images - nvidia-jetson-orin-agx-debug-from-x86_64.x86_64-linux = self.packages.x86_64-linux.nvidia-jetson-orin-agx-debug-from-x86_64; - nvidia-jetson-orin-nx-debug-from-x86_64.x86_64-linux = self.packages.x86_64-linux.nvidia-jetson-orin-nx-debug-from-x86_64; + # Build cross-compiled images + nvidia-jetson-orin-agx-debug-from-x86_64.x86_64-linux = + self.packages.x86_64-linux.nvidia-jetson-orin-agx-debug-from-x86_64; + nvidia-jetson-orin-nx-debug-from-x86_64.x86_64-linux = + self.packages.x86_64-linux.nvidia-jetson-orin-nx-debug-from-x86_64; + microchip-icicle-kit-debug-from-x86_64.x86_64-linux = + self.packages.x86_64-linux.microchip-icicle-kit-debug-from-x86_64; # Build also cross-compiled images without demo apps - nvidia-jetson-orin-agx-debug-nodemoapps-from-x86_64.x86_64-linux = self.packages.x86_64-linux.nvidia-jetson-orin-agx-debug-nodemoapps-from-x86_64; - nvidia-jetson-orin-nx-debug-nodemoapps-from-x86_64.x86_64-linux = self.packages.x86_64-linux.nvidia-jetson-orin-nx-debug-nodemoapps-from-x86_64; + nvidia-jetson-orin-agx-debug-nodemoapps-from-x86_64.x86_64-linux = + self.packages.x86_64-linux.nvidia-jetson-orin-agx-debug-nodemoapps-from-x86_64; + nvidia-jetson-orin-nx-debug-nodemoapps-from-x86_64.x86_64-linux = + self.packages.x86_64-linux.nvidia-jetson-orin-nx-debug-nodemoapps-from-x86_64; # BPMP virt enabled versions nvidia-jetson-orin-agx-debug-bpmp.aarch64-linux = mkBpmpEnabled self.nixosConfigurations.nvidia-jetson-orin-agx-debug; diff --git a/lib.nix b/lib.nix index 744f05ba7..174337f0d 100644 --- a/lib.nix +++ b/lib.nix @@ -4,13 +4,15 @@ # SPDX-License-Identifier: MIT # FlattenTree and rakeLeaves originate from # https://github.com/divnix/digga -{inputs, ...}: let +{ inputs, ... }: +let inherit (inputs) nixpkgs; in - nixpkgs.lib.extend (lib: _: - # some utils for importing trees - rec { - /* +nixpkgs.lib.extend ( + lib: _: + # some utils for importing trees + rec { + /* * Filters Nix packages based on the target system platform. Returns a filtered attribute set of Nix packages compatible with the target system. @@ -35,15 +37,21 @@ in - [system] Target system platform (e.g., "x86_64-linux"). - [pkgsSet] a set of Nix packages. - */ - platformPkgs = system: - lib.filterAttrs - (_: value: let - platforms = lib.attrByPath ["meta" "platforms"] [] value; + */ + platformPkgs = + system: + lib.filterAttrs ( + _: value: + let + platforms = lib.attrByPath [ + "meta" + "platforms" + ] [ ] value; in - lib.elem system platforms); + lib.elem system platforms + ); - /* + /* * Flattens a _tree_ of the shape that is produced by rakeLeaves. An attrset with names in the spirit of the Reverse DNS Notation form @@ -61,20 +69,19 @@ in } => { "a.b.c" = ; } ``` - */ - flattenTree = tree: let - op = sum: path: val: let - pathStr = builtins.concatStringsSep "." path; # dot-based reverse DNS notation - in - if builtins.isPath val - then + */ + flattenTree = + tree: + let + op = + sum: path: val: + let + pathStr = builtins.concatStringsSep "." path; # dot-based reverse DNS notation + in + if builtins.isPath val then # builtins.trace "${toString val} is a path" - (sum - // { - "${pathStr}" = val; - }) - else if builtins.isAttrs val - then + (sum // { "${pathStr}" = val; }) + else if builtins.isAttrs val then # builtins.trace "${builtins.toJSON val} is an attrset" # recurse into that attribute set (recurse sum path val) @@ -83,15 +90,13 @@ in # builtins.trace "${toString path} is something else" sum; - recurse = sum: path: val: - builtins.foldl' - (sum: key: op sum (path ++ [key]) val.${key}) - sum - (builtins.attrNames val); + recurse = + sum: path: val: + builtins.foldl' (sum: key: op sum (path ++ [ key ]) val.${key}) sum (builtins.attrNames val); in - recurse {} [] tree; + recurse { } [ ] tree; - /* + /* * Recursively collect the nix files of _path_ into attrs. Return an attribute set where all `.nix` files and directories with `default.nix` in them @@ -120,34 +125,38 @@ in }; } ``` - */ - - rakeLeaves = dirPath: let - seive = file: type: - # Only rake `.nix` files or directories + */ + + rakeLeaves = + dirPath: + let + seive = + file: type: + # Only rake `.nix` files or directories (type == "regular" && lib.hasSuffix ".nix" file) || (type == "directory"); collect = file: type: { name = lib.removeSuffix ".nix" file; - value = let - path = dirPath + "/${file}"; - in - if - (type == "regular") - || (type == "directory" && builtins.pathExists (path + "/default.nix")) - then path + value = + let + path = dirPath + "/${file}"; + in + if (type == "regular") || (type == "directory" && builtins.pathExists (path + "/default.nix")) then + path # recurse on directories that don't contain a `default.nix` - else rakeLeaves path; + else + rakeLeaves path; }; files = lib.filterAttrs seive (builtins.readDir dirPath); in - lib.filterAttrs (_n: v: v != {}) (lib.mapAttrs' collect files); - - importLeaves = - # - # Create an import stanza by recursing a directory to find all default.nix and - # files beneath withough manually having to list all the subsequent files. - # - path: builtins.attrValues (lib.mapAttrs (_: import) (rakeLeaves path)); - }) + lib.filterAttrs (_n: v: v != { }) (lib.mapAttrs' collect files); + + importLeaves = + # + # Create an import stanza by recursing a directory to find all default.nix and + # files beneath withough manually having to list all the subsequent files. + # + path: builtins.attrValues (lib.mapAttrs (_: import) (rakeLeaves path)); + } +) diff --git a/lib/ghaf-modules.nix b/lib/ghaf-modules.nix index 992a7da8a..c594e6098 100644 --- a/lib/ghaf-modules.nix +++ b/lib/ghaf-modules.nix @@ -2,11 +2,12 @@ # SPDX-FileCopyrightText: 2023 TII (SSRC) and the Ghaf contributors # # SPDX-License-Identifier: Apache-2.0 -{lib}: let +{ lib }: +let inherit (builtins) readFile filter; inherit (lib) filesystem hasInfix hasSuffix; isDesiredFile = path: hasSuffix ".nix" path && hasInfix "options" (readFile path); modulesDirectoryFiles = filesystem.listFilesRecursive ../modules; in - filter isDesiredFile modulesDirectoryFiles +filter isDesiredFile modulesDirectoryFiles diff --git a/lib/icons.nix b/lib/icons.nix new file mode 100644 index 000000000..96aeb6a7a --- /dev/null +++ b/lib/icons.nix @@ -0,0 +1,101 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ pkgs, ... }: +{ + /* + * + Resizes a PNG to fit the given size. + + # Inputs + + `name` + + : Name of the file, this will be included in the output filename. + + `path` + + : Path of the original PNG file to be resized. + + `size` + + : The new size for the image (x). + + # Type + + ``` + resizePNG :: [String] -> [String] -> [String] -> [String] + ``` + + # Example + :::{.example} + ## Simple example + + ```nix + resizePNG "my-icon" ./my-icon-hi-res.png "24x24"; + ``` + + ::: + */ + resizePNG = + name: path: size: + let + out = + pkgs.runCommand "${name}-${size}" { nativeBuildInputs = with pkgs; [ buildPackages.imagemagick ]; } + '' + mkdir -p $out + convert \ + ${path} \ + -resize ${size} \ + $out/${name}.png + ''; + in + "${out}/${name}.png"; + + /* + * + Converts an SVG file to a PNG of a specific size. + + # Inputs + + `name` + + : Name of the file, this will be included in the output filename. + + `path` + + : Path of the original SVG file to be converted. + + `size` + + : The size of the PNG image to be rendered. + + # Type + + ``` + svgToPNG :: [String] -> [String] -> [String] -> [String] + ``` + + # Example + :::{.example} + ## Simple example + + ```nix + svgToPNG "my-icon" ./my-icon.svg "24x24"; + ``` + + ::: + */ + svgToPNG = + name: path: size: + let + sizes = builtins.split "x" size; + width = builtins.head sizes; + height = builtins.elemAt sizes 2; + out = pkgs.runCommand "${name}-${size}" { nativeBuildInputs = with pkgs; [ librsvg ]; } '' + mkdir -p $out + rsvg-convert ${path} -o $out/${name}.png \ + --width=${width} --height=${height} --keep-aspect-ratio + ''; + in + "${out}/${name}.png"; +} diff --git a/lib/launcher.nix b/lib/launcher.nix new file mode 100644 index 000000000..a4f1742a2 --- /dev/null +++ b/lib/launcher.nix @@ -0,0 +1,18 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +_: { + rmDesktopEntries = + pkgs: + map ( + pkg: + pkg.overrideAttrs ( + old: + let + pInst = if (old ? postInstall) then old.postInstall else ""; + in + { + postInstall = pInst + "rm -rf \"$out/share/applications\""; + } + ) + ) pkgs; +} diff --git a/lib/mk-flash-script/default.nix b/lib/mk-flash-script/default.nix index d93586991..fba8966ae 100644 --- a/lib/mk-flash-script/default.nix +++ b/lib/mk-flash-script/default.nix @@ -7,7 +7,8 @@ hostConfiguration, jetpack-nixos, flash-tools-system, -}: let +}: +let cfg = hostConfiguration.config.hardware.nvidia-jetpack; inherit (jetpack-nixos.legacyPackages.${flash-tools-system}) flash-tools; @@ -19,18 +20,23 @@ isCross = hostConfiguration.config.nixpkgs.buildPlatform.system != hostConfiguration.config.nixpkgs.hostPlatform.system; - devicePkgsSystem = - if isCross - then "x86_64-linux" - else "aarch64-linux"; - devicePkgs = jetpack-nixos.legacyPackages.${devicePkgsSystem}.devicePkgsFromNixosConfig hostConfiguration.config; + devicePkgsSystem = if isCross then "x86_64-linux" else "aarch64-linux"; + devicePkgs = + jetpack-nixos.legacyPackages.${devicePkgsSystem}.devicePkgsFromNixosConfig + hostConfiguration.config; inherit (jetpack-nixos.legacyPackages.${devicePkgsSystem}) l4tVersion; flashScript = devicePkgs.mkFlashScript { - flash-tools = flash-tools.overrideAttrs ({postPatch ? "", ...}: { - postPatch = postPatch + cfg.flashScriptOverrides.postPatch; - }); + flash-tools = flash-tools.overrideAttrs ( + { + postPatch ? "", + ... + }: + { + postPatch = postPatch + cfg.flashScriptOverrides.postPatch; + } + ); preFlashCommands = nixpkgs.lib.optionalString (flash-tools-system == "aarch64-linux") '' echo "WARNING! WARNING! WARNING!" @@ -45,26 +51,22 @@ patchFlashScript = builtins.replaceStrings - [ - "@pzstd@" - "@sed@" - "@patch@" - "@l4tVersion@" - "@isCross@" - ] - [ - "${nixpkgs.legacyPackages.${flash-tools-system}.zstd}/bin/pzstd" - "${nixpkgs.legacyPackages.${flash-tools-system}.gnused}/bin/sed" - "${nixpkgs.legacyPackages.${flash-tools-system}.patch}/bin/patch" - "${l4tVersion}" - "${ - if isCross - then "true" - else "false" - }" - ]; + [ + "@pzstd@" + "@sed@" + "@patch@" + "@l4tVersion@" + "@isCross@" + ] + [ + "${nixpkgs.legacyPackages.${flash-tools-system}.zstd}/bin/pzstd" + "${nixpkgs.legacyPackages.${flash-tools-system}.gnused}/bin/sed" + "${nixpkgs.legacyPackages.${flash-tools-system}.patch}/bin/patch" + "${l4tVersion}" + "${if isCross then "true" else "false"}" + ]; in - nixpkgs.legacyPackages.${flash-tools-system}.writeShellApplication { - name = "flash-ghaf"; - text = patchFlashScript flashScript; - } +nixpkgs.legacyPackages.${flash-tools-system}.writeShellApplication { + name = "flash-ghaf"; + text = patchFlashScript flashScript; +} diff --git a/mk_patches.sh b/mk_patches.sh index cc641cec1..a927d2c05 100755 --- a/mk_patches.sh +++ b/mk_patches.sh @@ -74,11 +74,11 @@ mv /tmp/original_Makefile gpio-virt/drivers/Makefile # ------ # 0003-gpio-virt-kernel.patch # exclude /drive/Kconfig and drive/Makefile -git -C kernel-5.10/ diff basepoint -- drivers/gpio/ \ +git -C kernel-5.10/ diff jetson_35.4.1 -- drivers/gpio/ \ >${patchdir}/0003-gpio-virt-kernel.patch -git -C kernel-5.10/ diff basepoint -- drivers/pinctrl/ \ +git -C kernel-5.10/ diff jetson_35.4.1 -- drivers/pinctrl/ \ >>${patchdir}/0003-gpio-virt-kernel.patch -git -C kernel-5.10/ diff basepoint -- include/ \ +git -C kernel-5.10/ diff jetson_35.4.1 -- include/ \ >>${patchdir}/0003-gpio-virt-kernel.patch # ------ @@ -99,13 +99,13 @@ rm ${ghaf}/raw_MK_drivers.patch ${ghaf}/raw_u0_MK_drivers.patch # ------ # 0005-gpio-overlay.patch # included in raw-kernel.patch -- not needed because we do not use overlay -git -C kernel-5.10/ diff basepoint -- "kernel*overlays.txt" \ +git -C kernel-5.10/ diff jetson_35.4.1 -- "kernel*overlays.txt" \ >${patchdir}/0005-gpio-overlay.patch # ------ # 0006-defconfig-kernel.patch # included in raw-kernel.patch -git -C kernel-5.10/ diff basepoint -- "arch/arm64/configs/defconfig" \ +git -C kernel-5.10/ diff jetson_35.4.1 -- "arch/arm64/configs/defconfig" \ >${patchdir}/0006-defconfig-kernel.patch # ------ diff --git a/modules/common/boot/systemd-boot-dtb.nix b/modules/common/boot/systemd-boot-dtb.nix index 0bb47f1d7..a7e32ec55 100644 --- a/modules/common/boot/systemd-boot-dtb.nix +++ b/modules/common/boot/systemd-boot-dtb.nix @@ -11,25 +11,27 @@ lib, pkgs, ... -}: let +}: +let cfg = config.ghaf.boot.loader.systemd-boot-dtb; + inherit (lib) mkEnableOption mkIf; in - with lib; { - options.ghaf.boot.loader.systemd-boot-dtb = { - enable = mkEnableOption "systemd-boot-dtb"; - }; +{ + options.ghaf.boot.loader.systemd-boot-dtb = { + enable = mkEnableOption "systemd-boot-dtb"; + }; - config = mkIf cfg.enable { - boot.loader.systemd-boot = { - extraFiles."dtbs/${config.hardware.deviceTree.name}" = "${config.hardware.deviceTree.package}/${config.hardware.deviceTree.name}"; - extraInstallCommands = '' - # Find out the latest generation from loader.conf - default_cfg=$(${pkgs.coreutils}/bin/cat /boot/loader/loader.conf | ${pkgs.gnugrep}/bin/grep default | ${pkgs.gawk}/bin/awk '{print $2}') - FILEHASH=$(${pkgs.coreutils}/bin/sha256sum "${config.hardware.deviceTree.package}/${config.hardware.deviceTree.name}" | ${pkgs.coreutils}/bin/cut -d ' ' -f 1) - FILENAME="/dtbs/$FILEHASH.dtb" - ${pkgs.coreutils}/bin/cp -fv "${config.hardware.deviceTree.package}/${config.hardware.deviceTree.name}" "/boot$FILENAME" - echo "devicetree $FILENAME" >> /boot/loader/entries/$default_cfg - ''; - }; + config = mkIf cfg.enable { + boot.loader.systemd-boot = { + extraFiles."dtbs/${config.hardware.deviceTree.name}" = "${config.hardware.deviceTree.package}/${config.hardware.deviceTree.name}"; + extraInstallCommands = '' + # Find out the latest generation from loader.conf + default_cfg=$(${pkgs.coreutils}/bin/cat /boot/loader/loader.conf | ${pkgs.gnugrep}/bin/grep default | ${pkgs.gawk}/bin/awk '{print $2}') + FILEHASH=$(${pkgs.coreutils}/bin/sha256sum "${config.hardware.deviceTree.package}/${config.hardware.deviceTree.name}" | ${pkgs.coreutils}/bin/cut -d ' ' -f 1) + FILENAME="/dtbs/$FILEHASH.dtb" + ${pkgs.coreutils}/bin/cp -fv "${config.hardware.deviceTree.package}/${config.hardware.deviceTree.name}" "/boot$FILENAME" + echo "devicetree $FILENAME" >> /boot/loader/entries/$default_cfg + ''; }; - } + }; +} diff --git a/modules/common/common.nix b/modules/common/common.nix index b86989e7d..d9b62d875 100644 --- a/modules/common/common.nix +++ b/modules/common/common.nix @@ -3,7 +3,8 @@ # # TODO: Refactor even more. # This is the old "host/default.nix" file. -{lib, ...}: { +{ lib, ... }: +{ imports = [ # TODO remove this when the minimal config is defined # Replace with the baseModules definition diff --git a/modules/common/default.nix b/modules/common/default.nix index 1656acd6c..86777a710 100644 --- a/modules/common/default.nix +++ b/modules/common/default.nix @@ -9,12 +9,15 @@ ./common.nix ./development ./firewall - ./hardware ./profiles - ./tpm2 + ./security ./users/accounts.nix ./version ./virtualization/docker.nix ./systemd + ./services + ./networking + ../hardware/definition.nix + ./logging ]; } diff --git a/modules/common/development/audio_test/test_file1.mp3 b/modules/common/development/audio_test/test_file1.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..95d345bae6fa9b4f6360657da6d005a777e8df9a GIT binary patch literal 1362475 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Ǯ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

LrbS$B8@Ar2IZ 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%0AG>PCsc}?b@c%a-A5RO(!r12KOePqb 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 z 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%0APUC%BQoLRp@k;A3s?pHh;QeYZriYxyAY^ zpBNH`7>}~0uK=tBc$4*#fGp2W9zg;lyEgumykp9U5Vz5w_tl+MZ@fkUo=+VIQ<4}A zNFnSLNDiQelG8JHIcu`8$;3iHfOBZgkz0|V1hICUq{ATJ@j?rQn&haU6oelB@a>Y$ z!YWbfvI@?0OVP4q2Y}cjU&+bij+re86;Rot3>DEij&Rg`#U1b)W zcBNeSJgZi1*0i!d=}W*1Ljd*XnMHlpZIHsPcN#(n99ICWK_o}!Y(Eu}*A4>NTyIfV zc$fyk$si1ZV!))30X#`YlICT=c+5TYj2ww$;9PhDIGC1G%S1FVcT@}ANfVV>E} zhEaA$DqG zKL_no%0qxM17p!kH42|%Zo4n5q$sMe9mh_%p|hpdUZoX!zzySa9Lx43MG!BTPNr5f zf=TkUbjF2&JD-|D<&7aa&E#)G`=qyZw$@hsQ-u09S8ts;lA6OiNDf5TPGD-2ea!pO z=l}66`o$PaiAx}<#pHPa@zAhV#w57@wj{6D^8V+>94?lGY?^yN+^a@;0l z)NQXqN=N|!4K!v6j0zVKLPZ6K2&r?95(*6hxC}F=BaEyM%5CZkC#px+Qfwa76%|B2 zVIkrq9dI#e(yf*)9Ccin0Cl7@1r~|KU|L@7>I^#Z!{2Z8=kX>#WG#7iG#=2$cBih; zt3(GsltwZ~Y@|(F{Ow^ zO3TwrjM31zf_%BAT9#V!lAi{uy)z^M^RBpwgSYSYxlsv(8&7v?Fbmk+<&)8VddO&yJe%#CLiHD{C6|F z^T(M=)XT@;fM;t0-#?#*TfV+4b7*sYC@!@IRQnPAFMSvJ5$b&@GGmzY{16T^X2K+i zi5WE;WhnpnPfu3$`CMY0=n5Unp>9I4K>)n5Df+WxZJc>ipe(HW+%<2n=)aVO=2 zi3ip>Bzpz}mU5jKHeeR6%KMH$P0PIZ@}n?N2z_|S#TX`#TnS1)SeTM;TU$FgP(&o+ zmgq?3W!k)6+s*}1@b2V&CBk2<;V9&xj06@JgcluvvBA;pgEH6zeNj4|ClS60JEQMv zUU3f&>m;N~gD-^UhA3>)z|$-vI$|Q%#MRNp0MW|t?TgSjuiesRPb)9CG5fQ$eUXVm zo(%0L(+dkj3=FlCG8CrqWGzs882}iN-)d`DfzLcPWZZ)9Y;WcC7;SsW1ri^npL>T> zMiUK&IR$#>yHNiL?OW0J%~laFaPRrcXq{oM@!3FnIX|2mmgm7dn*T~mIW|bz$&!=? zOL2D<7`cjK>ETL4*IHwlSv&2*qdhpw^q#q;fS}QCb#qzoA1F+zk1yVg2?K#p$o2&q z4MLI(AcXe$!VVna0|H@#$zf>T;X;rgc9HRy702E0wIuLNw9e$VjMnmLP7W;ihE*7+ zg{)aWd6)sS)=)@AOEumYf`)IkaFhIG{^;_h3fCo`V1Jv=2}0wT5%Aj&)W1~AqMSP$ zcwv!H2y&veH5sCgP{wH!rcmNvG5&ZoBNM^2a%dM;I5IHI3NZahOmAA~96U+>Y)HDn zOyBfD^Rg|Z-=}XlM8j9Lfz;FZwyt3kM5=-UP?*y7%PB;g2#&i}H*RPigr6wgX9PtuA$Kw~m*Kh0W#j*>>`?X5x7pgrTp# za{Q*4`5Tn2lMd><-zCcwQRIF_!MryWbhC@oyYK&^AX83@EM|s;KCsfKzrSs1m)331ZGkhj?g1iRx`j(*@i{TU8c? z@sD3me$?rF%{6~TJrluaiE7g0eSFwx=tpdrH>Q+j!tSxlIX?Zq(|h5uh^(#?**)jR5-M44T}g$)vTaeUqfHXTD}NU z;yB_QT4Pd{5P37osA{Wnxg0y)Y&aR0u(~I%v0tTPYFEAi*jxTQm9E}ko{#M$K6tGs z)MdX_yFMOIToh9x9!%fvRDaR`=Ai3y0+55V*2#>+RTwoEP9#`hVT#MSQK=Fur3OL- zfiu&t_=CVq3fxeVd7%rk^gSLHTaSSf)K7`YvKTSzK@Knz7R_?W6q)d|p7uZUDUZ@0 zH>sDgRFtY+Q6F0YSZ`%ArZXPFU(JAm?k zx{sY*OpCva8~87p>OLA^jB9ImC3Onn%GyS-V`*cOU%WiQr~iun_b(?XtbvWw<#K~+ z1I!j=NXA3b)A$mr(SbmKBi;q49yfCVH}jNzo|D7f^TBA_sMQ7{A4AGvnSCk4-#v)mfkZCwVOq6)o=F~$U zmE-JUE_I%Qk~;}Rl@9vTeB;8wxey!5e#L=k4qt_8O(NQ!<%jwnRH~!bE(H(*h*6PA zrN4>`*~zVU&2|0pmeCKp;I2!GA$Pn9^ef1i&065#GS5nfX)yrR9L~yL{CwQD%sc%H zL6H$-Xj2QOgq4^woDIFPuGb+LM*^d*&rv$u2~tS=GLI*4r66J9I`NLA5GlSV<9L1k zYjD@1v1sPf>h%Km4_AVwyV;0DfS;xu07@d53hQUD!&i$hudQaLUm(*%#9Ex3t+5MO z!I<%;;GhAUyw!4tx#65u9CoOi21&zGS1PLK@ltR_S?#6GaX9md+gv81yIEHh1o%FW zob%J^p5kubt|A7?OH5Eqrel;gE*n%@X$j6eoRMFm0`%UB37ZX&jtsFxS_2{_%&81O z%b*q(Y%|(oCdeKqfSM|6MSs9mn@r$~o0@SD7D^Y6FvZqg$EpI4lOiDiMu3=?&Q?ub zzgXoD{o21Y{{a9=V6J_9Lr;QYU$H-EX<4BGntLqu0tu` z`1``y_3oBII?pmTx05#j9GO$lJ__6jDQg7kt+O1)ngE}5y2Ps3SppoHiRUt%YzKRQ zllGY5iy6lc7`lt0E4^L3nR8EV&jy~RQOYaQul2>rpch0t%vOe?C)SF!o=OTzxSbuD zne@=lQbjYa4su?7xC+SJczQAnQENDRv-!ZtA%qb6YVGH7B#Iy%g;{0M1)Ar2n8|e~ z()!tQA5@I?-p@AxP|ZNre}Mvw50Mlq$)}ya$ZaW_ARbjR73(p46|*YyL&0aK9Se2k z4xS-zwa$ZrqJ3~*S?|HhKzkKIU0qJ=Shd4#$+`NFtdOeJ~7(bJ341Vo}k8 zZIN{&`;M4Vqb2*}kk^yR-#Ssy;>CJ@2@nXD2tZwWwn-EjkeJEwtqg~0gLGplP@7~X z=ETGRM~4)zQlK(V!3)VZKaq67bqfzEDFus$qgF5)kZsSIb+kS`k-WUk)($gHGWbC{ z_cdm7Wyr^T3*DPpu{53rE{nn*88d|)SJk&?y;rJ!&jKb8-oQ8JJ(C*jmG@m*We+{Rp6&>PGJL@7u^@N4sZS{lII8S%w7F-8;Gd=-x$G zU-pTN#J;GWsiB?WCX+}6Qr9<(up6jfFQRm0SJYYhERR*G)3Bm19ctr%Z`e29G-bWl zcuZDmoX^JZHg%7u+t80{YPiU%U_=xEfWf*mzOhLd_$(d7n?vY&EVo1G;9k*y%x~eI8~y9hv9U1+TESaF?Ir?&c4P3~3v)4^m+l8HC$K=eIlzI$x z$MW}4ZkHe0x0w@vu!{zM)mSQ_jtF!`8SOd*Mq1&!B8xdwOVg}$1Z$EmDr3MZREWS} zXctQJO|EsSG{>|Rzb2SW9iDhddD(cHnAozG&9P3HW?`5bhkpH3-~z-50aN*6QDL0- z=h8ERU@OBj&~d6@3nN3NS_{L>16fF^FRcJ53)u8XOAAkEIdGH6Tx57(giR_Eqaka* z`7_B*ikaKGyp*ViO4P5g2^F_^Mrb|HBTi3Z(|>-_i(M?o&JQp_h!eY~QVs{R-Yz01y9qs;SOTrXX3JWp7#9<^?E&F0t{jAHAa;^YiEIHXD zZ#D}$R%Z$SBJQ8Oo5j+F1*1;zrz!}4NHIEhvFue$rw!}pBwT1*PVO~)tV`OIAWAegPTTLJ$w+pC{x3g;AibkFh{3PPqn_T7}CCy4t_|e z&>9rA!C=={N|o2O%acvN%AUp4xs8Ck zMO^IgXxbb&fLY=yEdUuTUSN|PV8oKiK)*gWBuGB3@PTAK6&~Ias3ZoZiM`C}&YMJ< zB`@(kWtOA|FR5|h4EG1xx9Cx$T933H5+2bIp6F?fk|3R(cYI;&G~&$4KXFum0~XRC ziKy$!FwwdCX^5-3DYRs)3w#j*xqt47#96IUC8nG1 zwiU!uK6ID}#Ga*`X(bjq5_saI`3b@s@ETn#D3hB_=_0gMxf=?v+RE_aECi7)ve7tV zq3b+wR!RXmVTZ`s4*+};ZpuZO;*VkZ_QD*m0=zBUPFwW>~U6uVi z<$&g^Q#(&(wI2)vqRanvT?C#b@2DBNQzB#|T7vTK$H zwSpS1!C^4(ywI07=2_(iW@U-3jwMdnYSHlzXu8IdE4q}}#ks^;k4BcgHiNxs`D#-V zALd!m(4z8`eRHBIFNC2vIpxl&dk$oD7ClsnlVo%h?$6DMPtw-hFDLjf7K&|~86e3+ z^wYYidoNolJS|);IGGu_leZ*m)7s5u2~)mihOoK=z`#^RFq671>cnwE$;9f0By@&& z_6}9l=9vSLSP;aJV~`QUqErEauK0tufnv0n*vvr4A0z|Fp%CBzORp=yk`w8?5@ds_ z%)oq>IRLVpwFyj48HM7=XTSjs`D%2jw5z5y??i;y&F=e9%YE|X6Hc@bKPzTVV1pw-Zz(2&%3zo%Q}yiZ9IpP zX7|dw7&RZ2B7&ytk*X!Ne$2#0Q=7Cu!|kx<*X!v6DBoRiV?ox(`F^<-_J30iQ$&~3 zEV<(9JXSEO2^u9$JzOG=OHd*soGRlABkX@KSHXnIO%~)QE{}?n{qC@dW@&%Q@^9V# zj}!T;(@Lu;6*fD&`e&L&8pxmjA2q_@?$$FNyyAbPM7E)W)#5^q8g0m3JL|_hT>$tGry&qF2BK_!*>PDU zAl%eG$g6=<@A(EFwLrRU;CAq!<_^rYn8ART+`Ck{N6)fsg&kz;@YaY`=1lfQPv=ud zZcpq50LK`)?BGcrB$$X>KYNrwb55rFI)e%cHEb)$l?0$qb-};tk`^n&N#sEp&k>XX z{di9lMC#f07Ui;ee0ypvOHq-(!0E_$e5U_Z%Z*lE))}r_lDbb+2;z^QWB8yy!A?<` zdBzI}58w_zBYMTFC+ip08LiQ3HpG!@<0lEITB)z0IhWuK@Y4DZ&zCV%*4H<*rDTuo zZ(zay10P$)!5nd4lzMQ=;F}NtVSof>WqQNxbQtF=6o3tuZ!|&_(Q10TlR2S)mYx|| z7+a2=51MisA81|@#7Z39mN5)vq#TI;)l{vanfNt7(~S~{k^z1RM~48ORg7LhXO)cr z6PTVxZ3c`%SNM@|R#iLo3M*^w-DG91SJb0}s1SAwUY&+e6<@r$jo!m_KlcozD$$nhSMClydohz%3^|^ zDyzjosL~K0c!?tvX(qq6ZQ3}=Tb6%MLIfJs?auxYH0VX7$i4x6PwcRND{1v+s^L8S z+Gammn8~gJ3^1D6%8%Gw*(5jz$)f#kWSZ@zI6m#)@oJfl&<{5T7geC^d;Sl|-@N7O z>nc$xkS4Ui@1Q@Xu}kLGx zo(@Zb0EZ9F7$z?qZWz}}AU&)4ECW8z_GO=UnkuyDZ5INA zPWsc)Qp0bGEyI#Q_22!dgF6T|;K7xonpEv!{U!Y_vv0yS+f9(={vh~sO?heXqD-6J zoBzN{kvspRKoqwq)k>4Z@xR6F2ugb0w5RF1+yM_JS^$s&O41|L%_5rtKbixF9KQmT z&l(e&mW2se4_E5wWOjMLL`L&Vv5JJLK1?fNRO4Y5A=^S8XHQ71wZ;>!Ab{gjxK-1T z3zSG$rKLMDid=CfGP=~>(W}Ed)CpG^Fc{nUd)QSY*9L9n@G^s z*bN=^OZsRDPJdH0x5!4%pHVAfDO$K<|1!#Roo;?vQQfa4ak}FEAbxs0alpQ7+M7lx z!MeW#o2+4LH(iu01qah8LurHZiRQW1UH1 z%9=vyoh_9m6G(@<_$>{^MYgb2QDL4a)_mP-*JFRp-{7J76iRD4Tk|%TFKcY$-Y~*M zQ2+VTC8REUa;IjsA1hjP!D}6g%tar=-B!Jswwf~Ig)qtAkw*U2kFi=_>rL(M`TCvR zMw6kJY7Bwfln|7on9?1$Iz|2V7MhpNlhW?EpmLtf)X9;n`*o)@WvicqAZBf>=Xdrs z_fAQ7#|aYSoWZ^F|bnRh-!M@OnivZPKtjeYS$^qJ{fu|u9UqsQlrlCMz>Pc&cu>Xw2a z<*wInxa0n}!F}*uOj6;9a;v&QPc|4{xd{J<*BuS{GJaRm3J`wMZ=9jXa1jNM4ow)H zwz~Sp+~ezoJir(tPLgGBrjR7)vS5sW(1aWjbBeV>Wd#$pwJzv8BBEw|5qi(Wug$LL z^8;H%eNCJ|l~#J^2#F6)b4`b}6>+cN$Vliz;l)mb+;t>7sG&97cZCju94zII^$4Qx zn00(sDXTP3pdQrny5iwz%z|l(w9?GgqO4iyGsjJ>-R)|!WcQnl z_T_ipBRqO5TQDAu6^^@jP!?#p|>+$OF~IukZpeLvWH;R%3Q5wW2>3NM%(Rz&>lMdt%T4s2&I7 z08?U^4f$!JOEWOkji7)_e)u8Vt7wmw9i|TS{q84H++iM8RF28AkS%|A1#ofseDUO zYm}DlH2~@@(+vSa24QSmYOE=kTw`&=l%}Wb7M8B_??Dc$gY{e<`FvhF>Ur9(+WKoN z*LQ24Y3*?p6dM-^O{;oi9iZu^?tHH8_ArB+)aI$TG%wHomk+P>0QXvRS&fyf{&e^H zDet+QRc*bwkJq28ywzX7Iz9MbS}bJlL3VF_TEsJrS1v^N^J|elwP38dfQzKpc&>I48`YGsnLP!;87GY|0E{ zY!h_B+1Y6xYd*SofVT~oCO62sg|4|7RzT_d^%e$G{PK6n%+%1R)BkNC>yxS&*!lI( zk9k@B{omL14zoN%{Sj6i`G1?H82t4=MUw>MFMP)`J&A@MKYCo&xVQVe z-n5Ok-P4`NJ{X^w)}A~4KRz_?maCS83Y(fX^K{gox>l9ZnaLOY1DQz7OCzgOiIpn< zw9bGjBpbz~UZe{1epRwWeeQvz{l=(;ArpPvrl)W4cqTJOi?(hy5*<!$(c113)XyV?;DYfo=uq7KwW??kPm1LGQg_HAwo?*Z%~nFpI+^NW?{xXcM|!EqZhn-}T_npuVAh}ETKh6lf2uFV6E30Hh{`e^poz1cb` zrH_f!9+m$fmf^OqHx6>?pC(NK^yfJZsJB?x6)I^oXStO+}A48QeN<9%=X zTj{-eNZN@B944MNg_}4O(`!o!TBQuLR+U8tzpSBAE}pHNeFk}NJWS?^!V@k4-{+b1 z)dE&_)KHuzld6a!A9#z4T0i3Yc3s8R?)BD`cmBBbr#$<5IubS=%0`Nn^+mhNmPUEM zX}@$QN`KtWI#}~GM<40$R-qrtx4%B$f%vKgyF>HLqc}BGevDJ*EkGJiwpN{yUkvGI z@Z)2%wS!?%p47KxE&kFw6>X9lZ&0LA=2$l7fuIi&OY4f@~fB)bq1!YeZ5ilU1Y2(|+5R9t6I9l0>FU${!zHZMt)A>B->Ps+g>aNpLbSVRQfw;XtpF+7Oc^s!32ZrALuvAGB-8!bNNzWJ^-=c4UtmNRxAr#71F- zC4HFsAetbMWRa?xeuhUA! z{v6vjI~84FJT^Z`QSSb0=)*s`CH2lB%lmhnn*e9ILX$KM`HOnF4N0cJJ)v_P&TzzU zN+4uzNx~<)tehX(*9yF%lVzlsV1*NPxj-#BGV3W`;?>;aX(*#D^`kCxMrDAI!Q}S5 zT^N?r2#E*Jv72{AY64|?^nc*`I1iZ=&I}py>A3Icudv?HN*6_ms2vzDU6Wc3N3>v& z4#DG^)H!Ksz0qNPB_)K#o5pswj4lbn+-8;4cQOPZ6eGvN;|H>d*)!fsqrv90f!;N? zY6dK-J>ND|pCObDs$;JX9-WgG5kjreGt=suAQspoqqM{GJNoMlKf24*HDL+HbxTKB ze+ixXJ@zw^G<RO<#!)MiRL!SdWM@sY9_8%xTpq3e$L1G1EHCT=ZTS8Cr(OhMxb zUP~ZBZT`krUP7_>lr*0NFg&$5#lEc>ozmHDW=6DJf>t%b$LZ(0q8Xa|+c&r7*sQ;f zk-3g-eH}07CYBi}U*eDZ^}ln(6yB(Bi!^856?U(-3XxkDF$01rwFUxlBy-&-B@jbt z=4tkbNq}kUNhBd{f!^4vR{q(_WG4=`%{4+=WW#v~wspA;g1%(yewt4+DSX1Lm+X_s zqqp?|Xsbiw;sb|TRxbr0nF@N($D0kY8qQ2%6o{~*^|ECv&=qX#H!IZVH)mDvmh(iz zx_&x_X@vgp!{?IuDct8ZawQPT25J#v3A;`qUk^mzBrf9+(j-P;QhWE%q zRQu+jnkrj^fE)9-N0FUq?XQAD)bN01)ifMlc$cw2ShR3G($|yz7UZyj1%wbStKUNd z!9^OIOpl{8q`F@+kR(IQ*Xnq@->PETGH}y-${H8zMOG-{^9XX&>xkm>vnmhjGs{KK zIVwKvWT>B6Xl_>>=AeXGSQls$#8>?efWxhgdEsu*iOnGZa>TrF10GTYG$y{}yA9E@ z(TwjjwrM$Ujn+>}EfPWctTI^T3F(B3KvU@;X*1swLsA(rPmNbUpJa#CzW!#8D{Zu? zDau~>jc;AFa_S$MnWmfF1pL!}-;J}X);3+!eY7-Sf*-%hxvAkd+JzgUkJSA3or;I~ z`7h!&0l*t)O&=!FfjA`EpUmTB=wM;GH4W~;3~bfxPng`(ze^ieDC-6hlzXJuBL|d1 zh~n9yJNekbzTaj5Nw>d$QR_z!e46@IcqXv$4*vM{n(0TNFF6N`M3`S%g^piz@O(aI z-^M%oVZD%M(dnD3rG-zq-ek*T{Q{p$74&LECmxGnYu}ZjDh8dKY6Q}ABb=qVt33@p z@v)@4x_3&eGE86-VRaL0p4(h)#iu`8;^|x#5eBgsdF^j$%*uj%{*PH&WHFBvRlqcx z&3b1F%3`vWP-|DwWFjV*9(oEngwiQp=03)75a~WnZ4pZu0!7HvETh4d51V(xRR;0tSr$RdC*B=3ze)8{JOF9b1nej_&C$x#8Yiwza*=ECLsUAIB+d(+xT(4rZdL zoUujO8N6@B-8FWz=iNUP2{~n52z4^G=?;gij1-^KbcPxwff^EEC}9;;CDjRLBs93U zXNz;>L-aMI-c$)1)wtLGF8~hi_Fb>qfhc|d3xR~Qbo6pD7psmgE%lgK+m$TYO#S$H zI_Cub>+%#@MqMhD3KCX%2+cUNMT7mo;rQ{wydWIDd1)H^`$?N80Ykf@^RG(Hwm6}6 z@5Z)&$M*^Vm?W*?!z8j7i7?sE0&%1cnoz%DL7yz@s&X4H>bxDZFYSqI-@(K;SyHz0 z!c2D^8=mH+Q>!f1V6Sob5Tajf1HjR0Y~6Q?7VmkV=hQgCgd%ey0x?f7WM##|hYmy0 z_ndE}cO^nrECt$GJBBRaQ_+wZY2`bxER|;o1DqxBbc8kb2G=4$RfOMtqWtyP1*B17 zh+7km_oD<&Z^;;_;~S6EOU+eFOl{5Oqbj=NY=|_B$ zAl3OhdyVVJwgero;>x{I5hZi4nn+%<`MfW}6{f!VJ65?9*>9ao~BJ%nbS zJJ(xK+u7Ogzr|UDN1!&Bb68OXS{xSC+r8pH@$dhsOQyTkty)y}*ng1z*Yont#|Gzb zwE|S>UJvqVLu_`B<6ZSR^LCLSNP0-SaWXSa;(g(^qpg)qosEEa#!y_neAPsh<%s0e zrRG&oxyeoOd(`(H>{FRmT$+1Hczpa~7%o=w$?}h?su#K`feJCyLb}Op+YV9M2}vlM z6{Vs)%rp5QPD{c1M1t|@jVRUMwTG%zi5bjJ zeY}jIuwFp<@X|+zQo!7uFX3bO7yXaIiaI(rpRWoaTU^P*bkA2DI z6t*#MO4eD}k&(D6?P4lbB@JF|ztUY;<*16hg6rjn7+0CdG<*yXBo*kidCF)2dQdJWb4WjY=kN6F z-5Bv?rQj0Vigoy==|>>pq1XAwP^a(k>}YCSA6K858C`W<*VS>>%H{k);KTL)@rYsP z@DUmt#qMFK_WNuNO};bh%3H$*nTIwLQt{g9hMI@tPy?hbct`w#!b`tBuct<-qGVO>bFZ@)$;n0l%ssbW-borvI(e`Fr8f*LUIaY?5xj=$evx z^>fZ^;{Ia@$vFLgr1HndbN$h8nLRtBiU*Z<#tDy!BnB-}BDTAPP-)?C-;#Qh!E3 z@s|YR(&+wdD-?J}e#GtmOt@(?vUDIDn@cXqSebOQZ|0$rNqO@MhjjVnQA_>r-Ch6q zk(8HAy%fD2Ir{~}PVPTN|1$tTt~01bFlFQBVzKr456j;co-DjHVM6CH zo8mjN(ZJAerELy1<6Re=That!46dY+_l%E zu~(TqE%uT*If5;_*3W9MIW6e=r9WL+?j^`o=~3FOUyHC)5U$IJo2W4e%G!`>edzaZ z%@}UWf}X>Hf1~JjRPR12p62iUxATf2q6C5~EKs~CV8Q7M~U%3Rt!X+kxs zK#cg9N6j2c!UNpIE;I}>(3KS(9RQ8l7c6$-En`Gu?8at_=d&FOGa$KC`yAp2a75lTq) z-tWkAnkt{)>NZtS{>j5n6}u7oLV)MvLHLv8*z1YtqWSIp;3NM{5#$M)~>GJy;rTO`GZ(A@1JgnOm17w#zW#+2kSv<10k-qpU(dwszYcV z8oSyW)2;IFx5@u5e#Pp^bS6}=yB)o%0YF(rG%Zcp)Q-n|)J6e-!30>{Xygc|L2)EP zYasIoT;rht!(d)CKox`pBs82TDZQc`tZC`6H7eMobG_SsA!Y^gGPU=mDqI#4q<+4Q zhz5uARSbNtr4*wiYqjC&8!s^ge0*r5syhRAzC0A`Lj&6l|1Ah;0AN1j@mnsdLbe1Z zk@35ee%*}T%qYioNk~vKP!5a;+w-UzPNN#waR3qbeBD|xJdusQ;K~whrG`FPV_}ar z0BeIiGmJ1AUFT);yq~Fo^YugsR&a4qx&1?cF6>drIqY#uaj64AqbsBdmgD8bJF(Rr zrg-3M$I@(^dY)ICZYatf@xL&v~$FYp93KfM+1()IsaJ_&<6lO<6s~SsXZ)X2!|j-rZwQ2DL^xhmetzT-?p!d7f(5gK48m5Jgi#uGVd{bB27#Gc&Dih+7&6aSEmmnpYk7*q?>Zhk8;XGhQJ^5| zUYc2&kQ_8b*@PoArpNrWK*0`#p5>BPtsdm`VRNF6r0<&~smcEkF1~1rbuf5YtXXHZ zl+w_Kv8YbFvOb-_Ef(k4QEK{jO#jlz67$5(e0XAZH@h7@-1sK+CBoX6!SX}fYrAGX zI5BX)JXr6}&kc8%P`<7{()P-=7M;ciFn^+#^d@;Lb| zR1nGAs~~wGd4Z|DnRN;mazHm~bUg>8uW#4$e+Bw3XcC|dB3&U%?7Xx4Kak$3e<`2R z0{cb`amuUlyi(R5y1-A-$J#X+LiQ8L3Nku^g#GO!iO(j)>mqkT@(Q6WLd`8hl*|mD zht2j8f)GWvxp3gv0?}R&soh}haZApDu#iuJF^Nz%OtwH^UebCyt&JyanD%=tDZ6Z? zx=gn^%c!(oQL&*O(JyRMyr$Y2X>CF1B>H~?5(0D7tr#qMH}5as4&_h()?>+Ejk~-Z zuJ&qE@w_~`5|F7-WtY@vHhc|Tx>{N=#wWxifhc~VFrgY7kOF;B^v{YbV8}c~kCQ!1 ziQB86)qtg2WtH$-2hs39#0zj5TI*_~!lI6)!Bp#=lo$TSDz?q~bFLtTihwhTDxJqj zCbJfFPj*J3!)HmHLssVhUANNMtfrl#-z!~G#p$oI9<)40Zg1*;RBlXZsqU`%oDz>= zc{L! z@Rn*SJ04M@=J)%7rAde74Yk7s3J-a_{NI4R?4aCifc^BC;(|h#kwc0ihxJHJKu%%V zC_SXVIREd9BGa zCmpAZC^J*#`H>?9(`gHp4wa`znWn`XX0EnhlvK3#ku{b>amlgg)Vx-9fo#nkSo-IL z5}LYMss_g&!R;pCMip72zkM+HxX>)=`9xH0Oyc310MKH(Ce*-9OyHd8UexAkRQjV1 zy7R2er0WC^;W`Vx$Lsldp9{_^FZF=5UY|6rrpoLdssPjxidwh#blf>CUos{kd}9I^ zEQhS>)b9T6|8}Om0MIy#&36PE4<9<1UOM796s^Awm+88D?@V+bkEy&dueGRGjc+sh zrB1n(zF%AZkEc&+D*Y{nU4Nchl306cT-6LZEpNcbcWU~eqWe{|+|4IaQ3as80>0YA zZ*%+HnX{FO5%gfwgO@T?M=NMp4AhEtt|W)`*5Hj)sB~oN6X_$CU?Wu%0Wa`v zqAtnXErOyC(aGJ>$)iyX0ZvQ_h`Sk7;x>%bw8U_TFrM&qU~ES!EC_t0vC^|730zU! zIR#NCWkJiGxeE#O65e4KH~R0N^pw>#$`nqiYdz0}jR?uG;{4vF$H9)qGIfQlAcJAfrDuu?6(biTGgSxb`)&5+$F{vH%v?*fe>Z6%m+9fDmK%{g zRr8M!&P}vwFMTT|^B9-7fNF`t6(L7({}xvYRvcw@+8+MJ#rubjHY$g_AKD6hI8?wk zj4U6`0~|9X<2L{v5;tP#M=@`o(61=F`8S9Rv}E~2D}>5Y#!Md>F!(`2Z;f- zt3>-@P1ikZ^2D6R$jK_8ok8c(RC#dBy6Hjz_?m#*cm(^$09WV`l?Sh=GK{CQ_zBY) zv6Mwh*L!X`a_v9F{mkLs*lXF46HrFOZOHMN<)J|Rwz%T0+o$2M^?4#@OggW7&XXyk zOKU3R)-*sM@_aaFEF5L@CM5o7P23F^P$cG=smFXz`@+$BO+9ow~6 z-gd*TAU>?>VBI0dJzB`dQjowbrK&?s)!vfn>PcZI9^b}A|iR*pL#P+chx1Cej#lz)Mvk! zu=6T#XJ9QXa+b}0Ys5duma(-Kcrx1AscoYYsUo6MA~TA11!%D=b$)8sl)(CkOp(ID`FouELO>D2H#?dvvHvEE}WEha>v1*HU? z`2~8(;%jYK10Vp!q6Rg=+4$ja%vy8MWCFC>-IP0uQ>_;h0+H^X3r?*WCnBOTWoLDC ziXSG!S$H=3!N|6&vs=|&Ge-|)UKJKTcV+`Bs0F`EC`sN0okr^ATN3an-FMjd)|zXb zH(0$>^U`kIdKt@?XJU11q?bjirk2(V#n@AFsMVQO(!D!Ldin;95w%FJncO|{Vvnxh zp8Y@70Cas6kFV{0=#FKZ!v*r$b z4D^`$YgMuw;C$6QGeZL>zYetelaqh!Mzt8Np6#i~id$eKTj)J7Qv0hA>7-));c-s7 zYLD5ES{tMI!ky7G1_Ml-WWR@1&3-Xl-kiDLyYu;PsbtkP?9lPF7gNl&?mV4`m$u#S zd;-8{$4IRb(P9c=s+6UNRu4_ju*Wd`rCl0+K4*`K1)Xh*$?2~255fuq-tE@IzA0*M zRZ?HphBGVwwXN__vaH7!eCxWr%uqgbJJ^FH2@brg-g>;1Fo{>NO9r@G4fTlggi(+k z>7ko1xyJbxduGv9U-z=;Ur4LX-Z6@%DXEmmkGSD|vEH+hodF;U+l^Pl ztT}B&1CUA<(TG7HQjOOvxBt$v3s!KT$Fg&gu&Ij@C8`Ovc7bmj(L4+IE|OfwL$IF4 z4Ss*t&V2NETszN(pn0v8irT1`C}(xmb{s?^yR8wX((~PkX;g}x8(H}Kf1O^?yGYfA z;{OGoEbG{V!~Zb}?=xKhfR6I2El6@8N5hRz{U8nvuj9v_)yMc(3$KegHjF-A7tzwX;%nSEBC8SZT*5i*47j zEvSR^j-ffsK!2Nt)=rLOCwCp~&TMwvndkV`*}Z+$`(3&`t?C}9UT$2T+|L$c0aLYFe|R^NH7^@!4qNGL#z@qZ@P zqGkg}?_KO+(W_9d3@t@P{PMF6-(|f;+)*W19L8)khv)K{JK4;sH2W?BWdO$qi_r#-tFx^@zQZKNOT5kX zDNGP4tOW*Q5fyaH2M7rVIzR@$T z@>F9@sY{4P4P;G94NB~D9BijTUR~akEv&YWocZXO`RINL(eq#Vn5cdz*&<5BE+~kO zUoBw`*18?MNCV-!y2Fo zEE@|_cMLFu#jl(_&5yQC=Xw$2pV^8Ok$YKHYmsIS{mm50MI%pjyhvurIj*^ApM{&g zgEX}{Qcop3b+8bMllmxH?G&xNqFi}qDP?0{M(5;__pZUE|BYGeTo#cK_?JFud>k6( zJ|}J7t@e}%v?DSTgC~zG_mAE;&Eu<2rGFw-ZZ09eTOZi+E7i(F005u{3|%8WbBNzhxYypP^GK-(vV zA<%KDe)RSUNw+!LD0L>nJ%ORcfQgym>aafh-*g|9DPny2u&Qb4sLweKN&^3LwPHnj zuH%M{8Ra|C)VS$)sPd^-;3=8g)&V{hdvo_L(Qi=S_2n|c5f}Sujt^10hMcq zOwv)pat8qu=ZA*7v3ypKCc47>i_>#C<@?0M*^Nj3R~H*V7u=@hdFa${Q8?kg>1mE6 zlHle=LIrG6o&^gnEm%=gD;F0ZqNA=Yw!p%iTB763)L~zo#C6Z=+|Ae=5rmc+DmXMD z3<9XE)hGmqO>Z!fBF_eMLTv!=$I?VZMRQDyhyrIB+k>P*W%xAPnP8nu_XAI)zDOY` z3|Ncp(bO4?hnqYE7KCp*(6M#Zd!c^%cf*zVw|PTt?j7%7ytmy{oP`QYvY`@s_l?{^ zCR(J1JaM|h?xe{s#miZFs_dJ+&dY3g+KE4J3DMwtx|hf$@_UHe>2X!04x~LG7GniC z(lngTjLs4k09uY%WLa*b*w2qd((;m<>vUm|b;DgzzIe(O7@Hx-PL|c&JtwQVGRZb> zkDolpvRV2ry5q&Hdj{EQ-=%x8hlKr-`c}-XV!cE>y}b%*1!oXxr+s=sR5DCCW=7I+ zXg@HG(zdSzYR7m1wIe@sDxAIr+ELgA= zg?A=l5L;o%l-|6Y>#PcvdeD-J)@eykx%SfH`_@JeOvT)W_=Q*IaAeO6?Oke8p~sl} zdn(77Au{4%B3VqTABLG@+*07bLy7idbR%Vo*%u0u_RIuN^DyvmIOy=Id`nU#MlI{f zk$X{6_40Uu2*z}Da{Ne9Odp|%{Y_~&-O=;ud}GIC6k)DJ`$Xt1k&x0>*>^hIQaNz` z-&>N4ZO&>_xos5n35i(G72c9A!h`W~12GrGg3^?lLzjUC)-*b_uO#Us_p_#G@m;BE4!4(>F+$3aDM{yv&zMzo zli06c_f^#W?k(?Lc^z9l&Y#`PJyIYy-v<6IMK1PbR809*_Ltf^Eb7J_`r9}oH1k~7 zbHg?Zi!D+J-+t9`R0hUNyB1$9yvv;1NA@H10yiwWE&n_H`)n|KcgNm{?_VE;?JJ1+ zzcWFKHuJ%f-Lw&w3X*I9009^rlZVl8WshCS}h2qE?F=%BD=y$ zRhTiN?>?1KupiXCxUCr#!pKAJW>BoHuzwm4FuC#CRDU?Mkr9=gG$%j?Ert}NoJ~^w zYav1iDk=pC5@bqQ%q}rBzMsXMm6DSswu_AFMJUoUtfoql*eyu3a>wY!GFF}g|Jt-U&)riOd2n(WOuBXINe#U#x zzP3!pJWlTu|BN7ZWlYk@Cp|X;rTAN-)jhaLX%gq6gT%7Ij}(&w(o}asEeXldLRH5{ z6;v9Nd}0Z1DuLpK*sHNf;t2(JBuUo2-=v8me^IqPNgIPLAnB?LmXy!gy7#v%W9Q7d zu!vrDs<+$XH$FdmPx$=Yzkj?hmBpo?g*DCHQo?ImSu21SnRX4$+Cj)Na|KO#NL#hj zeX&7JOn1!fOa|hq}GD;K6I{T+uC|RdNsb=l#f{b{?z>Z&pYx-OMNu;)%o!nmaHHBwWb8mxOY52?DXyi z+41#bRO>Zp*~@aG@Bb+9tBmPm-?38le9LXaq69OKyhJ0O*?Ln0IG?=basH3Qu^T#F z>}yxHZ+3p>YI~YF_S$TxbbQ5U2f6J`RLn! z`;kfjNTP}MJ1noH-E)pdz@ z441gPZ71%&u~lT)O@XP^(39BRg&N^6Ma|Xgp{TOYp6|1hR|)UW#U)~LahEVFvL@BS zAuZz9`289qd8T}m88iFQP}?O>;#I2LcF+jP!P`#z@;_KcnSy-yGcne!qDKqjR^p1Q zvlc|t=%?InmO2q)Ae`?`Bda%5OC9?}mx&U*vyZcxOR~?_Xk=4#U%BP$=uf8(y@_{R zYYqlg3grQm!mk&ZInJ0uId!PSQ7fq<|v*h9CGLs#6TMZY24 znQRg5nVKrrRW)n~wXaZ7*K7Rz?}%2vZ>7xD;&qv)|9MgE9Sda% z=z={133uWO7o`7**Z1VZUCq+xXAf^-$jIS*=4Lf&D3J?JW4<{Hx*r~5y&xo>89L^V z`ZM121_1I$U)vj&N7Ly10*|OH8op=1pqlqeXtcBHkcZ+DU)vJDp}(|bA+h@-k&XVt2 zSzXf49LHSeYsu$ZZ`Re38vr0f#noA&lZBbq#$;oa;6#0DGI?_U9^eKl)AILMCje9@1f4v!4FjftWiWd4fEqA_nFgBx0aP?92zRaMDyWF# znq^fFyEyTiuqv9y;BMzxUhjpE4tSxiPkC&Ghhf-JeTiW1-B-VX1M9`4b_1s-YHpP zKei0fWGdN*#iZ1-2QuX**6&pQ=g*bOOOrV|q&1?vHx5FJ_W?+nC(z2rd#0hXLP6tc`>PS@b3lWa$Cs#-;N->?HB9T;MxLiTO$me+@dFr?gP|u_foW>US!3I{iIE zP<**+Bs_&+DA%jjFZ9ksFS5mI5wWd{Fud=VzW!?Ok>U+eq`gA+7UArbJC|yWt zy^;X2v8a95M33*1>c$?Wr30Zl#75(jB;?|Jk!$yph1vc`Dd}SDNte`n-*)p90Wb&J zL^o@5CXO4XaR5JG5nd2GFiLD@oUAq)(toJoTsWr@=Yh1EsxT*>QJB5U2H$HAAqZDIYm zm#?ebCrGmCwAmyy(wr7F3N6AJ_LO5`;d?v_U)Er)OLpu%uXus>_C9TMl+{8DbsB4-XPV!7 zDXmnB=QA>4k-t%Eqty}k31g3$sdNVkD^_oa1_$YPs!KHI^F|&7;pl9A%UGGnX^BHF z5C=VG3F$DO%=Yn-oEu0RZ3qj~dF)6_b)oXmjlh{|z!XfUvi85Y|7`wFq%fWBu~zJO zFk)--GPk*(J-^Ianzk+jX8r?eFOEGrQb^v$bu8N9Hf?OC7z6|2G+j*E$w;bgg=;D5 zho_GIuWbD`(WcE}TJ=T>Bu)+ z@yeN}_#fg<13*(Omu+ZpUlQT5v-!+wek|VAs_Py0saY#sA7XuT<)EacRO?cIu?S&7 z%%XNh+?3AyNl;GA0Cy~ik!b$nzL$(flm85n(dRbPpxhR_hn(deh^X|tf7 z_{_M-kZs9Ptj5&beU1A+ywBfM(5SutE2W#w4MKu+{@h35R51AD>^t7&Zo$4^Xfu?; z+hDyaPwoQR%_*s@AOubX$)!F08+}`WQE;h?bY$&L;>v*105x>gxh>zfx@Y*z7A)V` z9c8dz^-uhy>*X-1bE@fXcY5@w)IAwKDNY&8fVy$JVOmIH-sQ;j3zzxq2rR=_1az9W=I=#SlrwPG(@_|<==nuWzPoVV5LQ=li z^#sNBY#EkNYfFmtnql%KB)6Ey>Z@|W#gnXw0r0?Z@)P1pCc1<;lv;VBr>cGK=t_Id z=QFFp_@3G1u-#@iq<}@o;)b4KFz@;1=$ohLhKzlrj=Xl^4#y^{g0xjxt<5fkujk(H zqm0zUWOJDPEy`3(embFIU>Yjz2%_hBO<+DY5Kg#?Wo!_lgwCcWwXSb0%6&sZP#x+7 zGjUYMd2rDlSxz0%ffBHb?m_}MjbLqa{x<4iT_`ZGG7#4K7+6}N2E7Up%S;lHD=+-Vj@eCH%s5Q8CCZ@E8yv^nQ z`sB25uA{SFZf%JOoM;osQ5(z#FQ`rI-39Z(VhH8lucx(qJ)3eZ6eGi}?Y5nr@Y&hl z_Ir$VAn*Sn?iv8vVX<^Uj;Ec7K;6qnlBHacq;o~;Mh(Jg!#W7fM@f zP%yc%Ia~K?M1|XtsZUs(5zP*C5ERM7N-%pc+9+5Ddv-EcHPAN zGj}Y%?-0WB?yS$Yx7iU^zP=>-_jH!Ofmc-Af z5gX`#FFBLb-W5hqSZV%dJqQgP9pdV5tuVEOv`KFJdS?&!Wo*lD$N)_;Li(!Wwj}PSZr< zT8Q~J*)(p!(m1VZhNs6N(ZpLh;<98vs-S=4s_@q3f3|I*e?>!lovD5;J&~K(Fy|-> z039BW!Ek66{nMX}8z_n%&Dd~a8O1BB*`le z&V1(4Vs}`VG76QuM6w`b6(u%^l2Mux(WoCE4>TPB1tA?c%(~14k%Ik{xh`B0N0dEb zt)+1H%{SBg5$ziN*`(+H?LbIQd`H<$%DYar6iyxRumV2&7h5<${Wbs~3V8)r^46Tv zx?ZM=V5!>jY|Z{q)03E<$(39c=S!EXJua0Yjnc)E+Jzrh@<<%QELLp|U9Lm4tI61J zcsvmTO~Hly-~Q?m02*wi?oEYz8W<-%!v`FPtW=_M#ppnc!bk1%R(-C0%*05-a6|7m``OHFrO>gx zNuvg!CTr5WN=S?%&IOI2cAN{M3mk%ts1EEqQxGh-y>5_dZ*bi5umBjk{WkNAm5)q*=%=F4eaJ=>=?WAlqOQ^9x_(m#a4yht3(~&x4rMK^O(J_JF8q#9fo1K_>#tU5G|M454n-_^IwWenQzLB= z;Ng(MT?gg5SusQI+=ZK(7*tW(mERDdq|&7Y?)fBzLdwyO*{hDRWjo5}uuoI8Y#^uFxr~SKxW=E(?)Wl*-906lS zxxHWO8$Rn=txRL04+mRb^0yA)G)ltSJ2Dtmi0a)5&bxzbW}Nk)lkC)mLBW;cjKQ6V z`D+TS*$S&AE$C!kUcu&@I#8v+N_0MiX^0culxj2m;w{_y|McW8=Qwa+(>^~}%{G2* zd-Xgk15jJHpRJ$))rR3v!@$JcvbqxpWspDj-BNdcrMIowWO-V_~wj)4;h|R zL3pHgbu{Dbjbv-fSl1%;ZvHg6>3+oBpa@hU%Ao2n#-hTAv|1>yp;0OkEne$WIZq{0 z72N{Cjabau6ee08vmbrc`ji~aZ;NY+T#g^gdx-i3rRAzG(OqZx07$t~eU5eZsNWhR z#l)2|zWdAS;)wiyxi@Ikch%@njhX-2^kD6j+*0)@iKYZClR2;oCXrzfqr`m*LTuw&{>pHVw1|?rHnHo}uf>Y*-pWFak9LmFE^FWqA z+SRiW8wa_f4-oAQ++B(lTk}<~owtp8S!o?PM$x!De*z zwf=plFJ%0LPkgD_*2=$B=|eO$`n>Ct9=sYl|5*TlG{ocE8;R5|fsvLL2fY{5SbES% zh*#D$LKp#GEnXig<9M3BZ(VeR6v5NOVug8ZJpLzSgOgF2+^7MJi}@#OPLZX4uE{kH z&L{yJ&Skt#fx0vC*y2=qgpBH3_k?>`RDykiSn%CEkMG7@>?;(0HlL{;Ml8UUDxMNi z(+!<}7XXH;YPm563CM^1MJsvfc$R_LF2rO)r#6fVL;wpD{8v!rTo1=IggQPRc#NSs z(D<=xJ4Q8~%6Alr-Q6ML>$j7E_{G5SfMi*6at?NzD0uqcWkM+_;B)AbJu{W?Gk&fC zLWw%aI|eh=DEW+B|Frv_CdS}ZJ`okXz4yNt> z)(*t7h+S?JH~5cx)&oF8jFm5}aNdGpxgAIc+K}0Dq^{~6j7~AFx*yXo=YNfOXrY+F z+mQzemd6Gb>P8GlE8#dMk)gyWeJp~^!^TQe5h|fwkq&E(-bE8<5phjXw@j4wP{j|x zbEgr2MFyx+%7$7cYzgDX1Kj!4mc4U~)hp&k^^l$dER3J)Uf-Hx}fh$g>RZf`n89!_L8z zw%qcS{w7NXGLB~Gh)=Iu#tA70N~W3NE?`@A`Jy1lwXSe2>(bZ)TSG3QQJWps9@j72 z%}^m;Wo#$t-G#k<#W^IHT9bZf=(I#As~K9?2*Jeg_zD7mfgo?LTM1F3(xA5?L;P*o zQ~OqZ;mz#}C3kfVFtbD82{qrs#wbH(MDJ^*IhQnkp;;7IIW)@-Fc#j#T(%^3D~5>{ z)Pyhzmxlc$CULb~QX%Y3g|i5=9EF-5-I)8tH%;tpSUO-ZYvxWKD`b2aMZEqawP|}R zI^Rv=zGnyE;nlrsnUWa^fsKtN29g6~y5@IQ6qQ54wu2(9|B+gCBp|g*8VMSoY0wT5q5><|Rjt;U)MQ)E;b-7ci#}5} zk>dniCg8Y0?JHTm+e^HMf;OZpN}IM$@y4Qd+9V1Dc_G2kGE+od=~tx}GTEs~#=0W2 zxq1`5lo8adWv2RnrRfp7ki4%w{NDNYA5W5rH}Lxr=I}i}aSD>vIZuE@(HU0K4lX9Z5yl8Vk^EI8pizhL# z+)94ykFrT12C1akd3WP!^PakID?h`RS@t7CD^AJSbL0)VGk)k^)IH>hBsnD02lBk+ z_eFEd13!#72A}Nwt%zQ*ffG184(^NismmclPm1}FS)kFYW)7JS6PDg zTFhUSh%ezMhl>(<+;^V}QFCi=8?Mir4!%|yS?v?2R} zC6$nyqRIXFjkZT6{^Op-08moP1#c-_$w1pTKE{C@B{vdY%X(*= zXf=(x<4jvtUo287DdNdBD^k=tDze+mPDaC7!tgTeP;>6M-2Tn%VCWSN!C>Sn0x?tF z*#?NNt@ygpsf3L6s1Q58-kk}OR#n%4rm;u?{(}6=c{*EiFW4FJs<-p|SG=dpg*yO{ zlBT)$cLJljRlsBe05~i%dPeAW&M@9IkQ#mOFLY2GVz@0Fz15X%gQtpe;?SWvEK%T) z?czR_s30dI4|=L{ikd_}3{6##6_ulunOTPHweW7Syx;+H2#fFKwQW__!%&kb>d^H; zUx<~gqJ`cFIMD{-UN)hKisE5+sE2`bX+nj6R@cJQO@n-0GsC-A4WT>ZQw}YI0lQ)h z001zZZ~8M;C}Ydv(`n#!xBDC?jN3DSsXFjW0soSLUT3Sux$+3@I%Bg zd0LWjbGQ-*QL7@2Ic5lt-SR~9@OB={KG9s*NQ{+8a_FadLAHUF+OBBnm;w2i6wXM+ ztJEL30EA_QVK#x)kX9v&bZpzikzk2E@Fyqk3zLd!H7aVKk;V}H+ z1?fDLSHw1lU$>Gj4OKfw_-D}bG514nEkia@BRVH6GI=-tUpKv3KHfgZ-v(Y=7ggU` zBLSaNz3k19=`(F57EAZ{IU8DuohvUdOSit(Ez?f-KJORP+lLqQjyGHW>!q7<_-w<= zO+1gzJ$2L{e^b6Feq7i3{^Oo|%Y}C-+|y{NB2-Ke>HC-&fDT zu#6-K9>9G+zOAqhwyo01?Cu~w|B%c;AeP(Gxu767$xH&i6j*}&(4|ST%3=zLQ7NS7 zT=x$_G>OJB71VoD4DT@pJ6aH`WX1H`1BHDgA?)hJTR#&wd8CzTZK}8G*kbQ-k*bjo zlmwzQ&=lhVc?4ZPzLVaYW?{IC*9Lat@b*qyTyh!PO-U;~H;dgLIvxj_VLi1u>5V31 zF|W%7vAkpHdj_CPGD__GOBXA3Kh8!!x+X=9y5yXm7O&SBJ^+dkIeWUev)Fp|R$~)@ z@Hw6lHXuySg9Ki5qLar4dL>2}o|>2$9vNO1STUO@MO&zd8skS&MSX8y^qWPSG!RlR zkk2}UWHMg}I2^Va$y)I5ij&|J(=ZruK%lCYG7tzPI4C+6JUxk4;EGLqchWp{z}>>6 zB(9_kL^y70^@JX!NF))8vV8T&%<_#0s%WJcO0pfZ%POX ze9jIL)~q*GyFSXLsC%k!HEC>vC#BufKjgXZI%tB;?^6jrZTl@N;UUHRO;;PS) zed3aL;b1?lsK4Zkonj}AosrqYzpnc5OsB}U%j+6zjRXQhkEs!$X8Y#~oM-Wt_4^P1 z3^#XpF#1SuXzHv&rbW%t`Qm-r)c zrtXVp9-XlN2H{=-P|p!7UpAyblnFQtlcw=xtt1Yw-H4*6qPG5Q@sN9|5c&puh!wPd zcg&JBMe$&|YV@~Oz9vt|YwmfF?f5xIis)NBkSSyyo;8Xm3*{74l=(%CG*t!=aa>$E zeq^oYVjUNa!&n*w|0`t`b`>xODMU4YxaD7|zQllop+2 zjs&OIAlZ00rA9=~$$Yg$L>&GLiVWT#YX(kE3yr9Z>tzNstO{g0G(>z%j&3|0CYcyC z4hpD`%s!%tPB@SLeJ*ZC%@V-d$_1 za-^x~j3e`?>~&`VPoWEeFXdhgWGrv(94eZAj1B7Oqr#=e`oa0fg?n4Kziz#> z_Rt~ith_B&^>(6h9J9q!?sQqpo#$b_OOgrlK1zM?H}YxaXN~zC^a50WhT+z^c@f zJAo1&FzP|G$X^V+YsL)Lqf+)-*{`Mp3zTn~vt47`uuGv?9=@bR^_LS+{K2>MTx~E* zV^JUS#%fzlMiHHTL|W>obnU9%O4IFFaVn7?>@a6-qLnB>!&mR@I*M;S#-d&dNZLlR z)*^@)DZ3fjNE$KL(PmQ4-9~XmSh&wcx^VhVZQ}@B@HDjCt~zh=At$%2@chG!WhU+L z{!zC&RDGB4>l9YdJB^USA76S$Qa&iWnEPO!2&}lgPU4R>O1-79}YS~FGfQvi>cXC&o zFd5&VF-v=8Q7@h&{_J?sp57Hhqo98$&La|9&Xpj`ppGF`sAhU@^aQbFAmH+)`He^6mKj#r~f#Dl!0c!$9}jEFZGy zYQy)K7l3rt^P}p}G6v_U^?4vz{PO?){(64?{(5>ZE2+U}t6oCy{ku|1lcSIJ^(X(U zBg#Y1725Io|HhjS4Lh=QacRO^T>J9pSrQk8>C`?q|EV zNCCpU0i7RLiDmb`ItUE9|Gqkv1ipH=C--#B%c#dVOiJ~aFWjnCh6 zPvhEj*dFm57L`SWG8?k{Kl~O=E0pmBMw+RaIB85wBYrzQR6v1%fFhwu3I3HXpsyO= zLezK4uaowU;a8qFO+8+;*g=22PL`@6ogh{jvg9{G3OP#Ud6w$L(tI#NBv^=r6-e2V1r+TS`u$ATIxJkxWrw=9j{Qfn%8JbQxOyS6!qS8-2f#wD3%gx#7Efr;K3)F=)w;>QwSP#7#z?RBSRQI+J zu+%F#<$32T?Jg54RLDnkDX7#{{ldj9dzDd z=sXhXHVlkT1Q6-EejXvUeKpQ1w_@sli}56G1Fod_%L1@$FJskYSR#W1=U=apd0u-} z;WW=EUn@c&8K|22*U~IrImN^tgM9F4Z*7lthjisdk>tjEy-05uCuwu--A5D7apfQz zl&ppnltF6MD$cuEdp|ikQ9%*W`9=9-_D9p_>}_X)sf(*EFpeN>?O87PtDDbcK4Z19 z3s2Tui5*4X`bm#(JjEgzRytgv6^h4YhiqP@6Z=B=uC`3I^5W$cSjIUb9=Huq*Gle| zZpX@8@yMajw>wwgM|Ql#7p0WXT2v}!X8<5XAOKL%G&Dq}J?0%$J2ob=r{%)pq@v;) zfzaO?PhL>~^p>Qei2${EJ#b9LNX6#F47f0L4erg~X1@o@@I{8lZkMDG`0^l#Lvr8dUO^{03XHUH=7vj>~s-yEMT{J;J`0Ion$zwRfZ*-8zjWf^>(^(>m+ zOj}su>TcbC)I_~Vt19j~zRl0;F>On#b&$*4;r=Th&W|3iJN)vH49OXYl4Qip3Irp( zprAJ>*j*`T7?tZxR0@U}SXA{}1E9^bCZBzx!ah1YHU`@YUNC+fSm3V<<0?$^_X|GT zeGZ|ZsLepWG{%X5S`_UVEBx(|vKhA@L>Y;QI;sz%j7;pNfzD{pKl}gqhezTKqj8?y zfA80$aaV||AP|TuCICho1O))KT|1go2XAGjU5 zqQ?|>Q7W%&H0nY4ODD6IK^$s5xy?u3WVtw;!w@p-QRZPY#)PPAWi2Qr8WnR3z5n~5 zME(E+_*z?g3oS^aPfCn!ZM+Yey?MzQwx)TI0sP^7byV=GXZ6-3iq` zC68Ixy~OQ=L26T?SxzL=MIuA+jmMWpQqLS%f1JdJaBRdFmN$7b)$>8=s)3WG}exI6Y=VE~mMU z?Pxqh6z8IkAF0}%**i8ieM{^%FQNQ=?H8@+`qi}P+kOrE zsm(R23b_Ndb&p90DYO+%uc)Q$ef`s$@>}s(?Crq)8NF>Fs2zCE?)JiK@+Tt16<7(yPG{l*9Pm4i`n$KsqoovfxWfgJ_peM>Z)az0G z@i~m+=PRf^W^gnLQX{?8Vu&i5a*#HHVlTe_prl7z#qOCG9mcA-DxAD>K(CxUt`%AHZaV4xGBqR%;Yn@#RH2`mCi#28y1f84|S- zR+#r2SD2}Mh(5PB(qHxOb#0BDE1>tNGs|j66)NlaeN*!PxBstS8}G{9`hWbr88emt z`>;gJ00bXzS!*vkSelMH?PX`L5B0}gtgN$wR<~<4m6rGsM>OVrD*zz?PBsINR__qJ z3^?^{$XFT(L63L^)|As!r{pb0ClO1EW&;gDPeR?(MVptmT=9XV-GOgAB?RFGAelRr`Qy6H6s zK!i*y5X6itrc|t1+FkD>(1089_R_3zT7Bt@x$PnqLahP8gb)df0Cw2mKzQ;RT4-?< zv+B`1P2zRb%|ZsTs~tC?YUyqlG2LLBHZ3YH>r9W znM{1w)KAq!#m1S1{Ff-piZCEldoIT7&nVr~6>X=hBuCH%Ol$S)`$j3*1xs;vcz~^A z7Z6)UwWG{f#Tl0B+MDSfxk}DekV1vu*0A zNUJMfDm82$2RBe{%4T_Tyd%T;W$O(3zgIjxJfERqI_lybqp^w8%<}>dc+ewIM2#z| zATj4U(VHC4j?(&%bHs9-9we4&k33wCqfzlj?ai?~oJ*m2N&9quS6lQWYO40u-8@@6 zlmGj$MA-lYJYLpoSUGT`ZaS@LWxP=3$6u_Zw8D9}s_Cbpai@cvHMO{g`5ZH>2usD3`tL@2+tPByv zLL-R!rX7Uz#E3iP5BUgU5QVu>(L@_H3o{FJ%pKH_SZ&$~qXc3N8DjwgYdc*CGNSFE zi{k*(<552T7OnZ?o30=7e_=me-`(8&_XH&Szy0w^;ExTI!|_*(5%823}al9Npoth+=XJfj-s}N zMAo~;v9urLx)G5TXj#1FuZaOE98u(1nkqzem$SX3n5yVx3J3rq0&>XR;R)+>-W1{_ zE*fkrgDA#o$^b@0W|6L|hP`lr9{LbiCE|#HM-)~}Ku?JipiEMy_@*{l`E68!;_|ud zj$xAniCcj7_^h+VNsn{2l%n8Fd^k6|ni?=t4py4J>R!x#T*(jz94TM$rwUMpBnTJe zfzt;6`@lrkfJAFrS8FRA;HD1hs%ZnvRmIy|tfdhP47)0+tT3rnbuVNHfGCrEZSInJ z0;9}CsmP)yx~o{r6$`+ih7t*%NKYA2QOm}H=)HXpBv?8XI;1cF7bJ<70ZxeoHv-NQ zvj!}R5`*^>HCq5!bYF2X%3<-K*l3D#B{d(ch^b=SZ6Shyath>iLnfkr)jYkZZehDjDIpweWbTYi3j6r8jP}Y4n;;+h$-#-JA3C@12l`&^~Ul z_7MKQ-?!@)QYWgd6n5Y2wN3{y?WKowMQQ0Ya2CyVBI*##K~Mm}s1X$t#Yk-wT!e!q ziG_|HD45`A?$bY4T598|J9}G|xp1cvcF*;gA`ylN^v2St6OR8{-NnzS{@N=Lp(5Or z{_0)ewit=$fFOJt5xZNyzIg4@joe-sPFx>mgj@yLfRX1CKJ7u02B)5wvW>h_MG`#VfcIl-AV2^IGyZx<&#kni zR)fpc#uc*>f?|325zin9KuM*jvcGiC9pAsS_AK-69gkT+ihV`A?CeD6ZG)2QD8!jI!MGLHtP01{ZC(GpI5)oX$&SJqgd_VshI1%-~b-VGu^DSD^Iu9w%o@N z12W6goW@`US(-^;kW%Zx1DFvpXelOS5vjOl)DawoO=S?%aDzh#1-$Q0Ken#Z#Uk{S zkNf{mhccUeKEz_~w@n+NmQzrzr`E?Z)3f(2<)TLzl8PaywX1bk*%>j)j#gDnePB*o=a^`Ud@pnT$EIPLw0BGoLB9rN`AMu23C zrrcs;Ka{Eu&TAdUX6gIg@Be=W42`JcL2nMX*@rfF?v;1eFN!L%?kX|@O|Qvr-a`(l@j*5~O904lhF5Wk`o}MiA49vZbaHz_pSIFvAQ+ zk%XH>GNpPZ1|naG9mRp9C5nM3he(VPGbMPS5?%}J4Mk&eKUa|Pcc@FDf_k;ZqFtRg z3Q&pWNG|ULa9$5yG>^WvsT^0p(ZnSUcRlu!aBYUK-NANRzc|#jmhL~~iqmnDaLxDk zeqZ0eJ$W-1{M$y_T^%4!WIDE{(rPr*cRjBMtWFGuhDvI@u^cChD(PgVhubilNvW=o zCP2fA$r%xy1UV|&tz=j^rF_d4GF@{if+vXM>3F_L6tYi5^-(Gg1gBZPagrHJ)h>cC zoOR4Akd53EK_^nOF9#8mA7j~5XmkI*b$=uMYQWn`Nb^auZx&WjSW>pa0wI=7AVoJg zh(g0PDW@_~6_PaKRh7-y%U0d=tdrT1Y4g!tNhZAf4#?L1Bxw+k5+hVpG(@v4(^zH(Bc`)3 zN_6yrk}X`jtEM2~P1L8B-hY}EOrOWWIAO^X=U;KzA~;7WGXu0>h=W%^)Rkm)sbC|r zxW_oEF^0mb|NFpX-T(!fbK843NZPTk%THm698%4pZR{j=;qo)Byp#^?u+EWm8pq>s zfdC3&_QpAW{Z=}pet#o-n;`~oH9!CV`_h$mtCH$hCKX#?!}}jQ{;BFi=EELXqDwyD zlVbYJO_Y)u5!qy|DQ(WFb}sHYWF!f*rI>x~raN#bN&$(js-~Mc$RB!=%^uuVFnowY zeXSzkR2H(Rimt6p(9sshcF8+1Y~xF78qUqy#5UC)tMWTHe&xEC)AIZN?GLJIR+eWh z*9<9J<0)}e|NE>uBy$LhA%|1tqT(ej6F63NWH_p2qLZ`~Kno?HMK2=`OA#=VQjW$k zEVWD&3dCRXsY?u9P~9{GPRgTHx|g#hZFM<60>Q9Zva?9~iYf-8Y)G|6J1&Hk{Id<# z-%5D>^Ni9jpJg1Wf9}87vkj|$VX9$V$t@r9r1X!}$wHYm{KqVe@{sgn{%xIaFExhC zX7zq$)lV`r=BC7Ohnd~3OKhX(J&pZdLl%3;+Ht7SI@xSqjcKL5*v|-TCS5B&acWX| z^A8fW9pnAF)z;6 zW@1)b8%Qowx`Lc9fH+8UL?ZzaWp}P%0cRM1s8X>{4KkqEQfq{oc6S;3@r>^AGvu={ z$cWJ5s-2z^eB?+1?q360;Z`Y+B+&td&m)*6u ztzAvDwM<<~QC7D+-Bew1Q-Ak+TusYVbv5;K+toeI2?vG=yz|zWmC6X-sZau0XQxuu z$s|jEo>|qeITV%Mr@ZjItR98Jp`47;cAGE!v6!F&lK0-HpnGxLk}9np#45s2bne!@ z^EC{nR*vIpr;_FW`=Dg~fCS`c*ZT<_+JFq(4LrlUQz4mO>^%)48#3%YgpS;6OpKu- z`l0o3j+<^QM0=^q1ZuMF;;-MMrvpM>MnimKO3@7&P4C@?~$3e)Fv{H7zzs~ z+OX2t5(6xPRe)%inRQDo*2aJj1kNKyl>sJFB?=fI4j2|4f|#I7LjngB(3T5D;v5BC zBH?Z_bJaTrL^UJlu9$j~rOwcy&Xg({*pLK_R}O$HiB(llM&rVZUBb48TDz%t_vMg{ zS(v}wBl}(vLIhj{m$Bo>jtv&Ag96kyQY?6Q(iX8Y1`B`hKtyzCl}T*1wqQP|z9ZsS z*3O}+k`UzU;Syzy5sIh{xPMQ><=(v@pi?{4l*Sk z$Gc{TbZIHbiVP(*^J1dt2s;l2(Y?cxmAI*a^Nz)YX7#Clw&58O%f9CLdT|P)TQ?%n zYD?bhRV!6dkp4t;bSaM1WKuGWDJ;H)&3}=!o|^7dOPq@8!AOcEIxWMjR!BS-hfp8~^GodKXBg%C8b$zOm3z9Jl zH)5+ILXVgEmWRBPiAtx%=Rf?ec1pfT zRE=Si6sukmwV{I5K#r5d6C+BP8DU-`4exD{6C6Mo=7CW7SptH{j7hZpN@`RRi3p1t z$C9M5@@R7=$pDExOUo0(T$qufLWgFh)FV5NqGXhI+7sM5>DmFu;3@IPNjk6qR$ zqU^J;R*X8_ofCdMc?mWD`=Dg!Bi7OJhlkv6Zg*Ra}on~8FWOfNGYd$^VW!~_=|1VC`bl2Z=^q`1~ zr%BKfxF-njF)-?W`KG2{f9fz-hnSXY_Vz@>kVt|%yNQG|)~aNhFQ)r<0}C(#=ycf~ z7Xq{#^Eyv`BwgF>idTX8qTe}T{(x)4HD}}X%4oJx^GR+yPTlKn?2&?I)?^eVEPg|N zM(=h@EZlL{?> zB_f^v@B%e6muK$V5du|*(n5IW7A$7z0{hFJ+>&e z5MlIF3(PsFuHVpWMx!d2RfYCo>}JA2t5wf0vd>WG`VrAb^&Gy8kKXNFyGYF8t)C>B?SS9nB?r z$kGK?Zb)zsE(9=Q2qe&=#uy4kbb^6qNL0^LT{YdVwa5k^*07(I;V;7TO^bIA%!V$_ zWD{(ALG-Y+5r>fnuiw?> zCl3XBg?;{}H*)dbx=`iY9Kdv zr`fIS>7odLk2}k*6NX}Lxz8T98ABHg=!yk)@DWY5d$@#Tm60ZY=+&2={v!~e|FnSOu&m*t*oT70>-L&@;m ziPXr^0aW$aW~xXMVc8WH+hl0}`>6@{=NkdS;4>h-|!s(aa}>&~m3up`kOCLj)*2EMII1pdd>WcHLe(w8FV%_6G$JpLybA_cz%=626PH5D@7-T&gU6m!y1XQ(zLTz-_5?{l8 z0&gv!35MiwnMqmNqiQ1jxl5C7s{i}2Wa)qe$96uS#_#mvQ}~dO0H(RRW%U2&(umRXHtCYWBc;=lZ`6ci zn~Qgdtdvpx_f`j5;efJGb8}9p$eMesK}0YNA_7wR(16q$H1WQlNphAy5W+10URiCP zL05gf_hy=7{_W+uUVSB3|0owK2*y@AvYjCp)bW4Z}ASTxC+6OBe>&6!GVm*r7s%h%qp7 z&_bQa^zTXSlAXFDypi=%_=K^|hSvHwshi0Pa3wND(3gZc32^gJn5F6QatlGmg8e;i zI#y`%@sXi^}7 zDvAKokOUdsM1yvgcGVYU^O@SGwQ?Z-*OyP<{9CJ1fnfU=u)V4X^SNUz@_8e00+_Er zQiCG28T}gSrGF(DzUtn&>2&PpoMHo-mA67hjtE+JovYC(5+^5iU>EWRXKm8M=C0RC z9O9}N5}LwwwZ?&gM+n1LZhA^Ftm(0X>DJ1yhRGyzn*!pq*Hy;;=H2sjkoiKsu@cv= zel48Z(05B-dFzeKCL_Fx`#OgbI;HlSOq8~vLyZ-3sJ8cA+cxx;O;WNhvi9>vz6J^z z#Q=7N5E39_m7+kR&e<@paOUPTV@WT{b~ELLr7aDp+CD2Rha0u%(v_sb-x!Ov1x`r) z{&%})mAe4yOyD=u@nxHQdV@}XlKst%G)WQH4VzPF_2h*DmJ(3ZNjkAA9Fsu6Q5^BB z1~$*()hbfw6-F)Bo(eypaG{CjD#KP z*onfrvP&LYEgcq9P(gRj?pd@quAav7?Odq!yK{Wzq*4pCpK>bz&}Bl{5$*bsyH(pY zdU|SFNCUWuvbiYZ1%*WpfxzmWDHj4k8>3?;B=xtwNs^9?NFyrQS^A9}%ClvueaNB7 zsyjfmQBP~N`5}7^2u%%(_dkQ%G*mdnv2J0E06S69JSu=0M}MjsyS@9 z!#X#U_gKX0dX(oA$qEi}ixxUV@l=#ha}igJ9AdILBAV8?#p^9S8M9JpNhkT1fFNq`$irpm8|-oVFT9^frU@4 zq^Y4ZLQOl67DN@gxQSWs$NDZu5l2JG%A}=yc*lg!471hL2i3T-?E$(Ff*R zyEgWbmm^*l8G&HPjDHfzn4nn$k6t?M@~o2;8JPFQwXJV+gjSZRRaOoriKD=c0~!iE zjfmO5o>%R^sg>qaGIaTLJh!ylU;tP`5C9X` zNdodrOsItyCL?1j3Jok{_UPAK8%J}S=Dtn)gwoID`R={`+@;Pl^w&v?wSNFziBsLX zN%v_0gxv8Z+Yef^G^?Gw&RBwJ?sOiZL8Q@E(kX1w^9kSZy3*;yEDYY7*2XyGTtsv$ z_bX(UhULU4psn~%7-g;UM}6688oJgU{%o=T|HZ&2nG9e~Kliml5aK+93K9szk}$=P zCOoJZ8d|GM*w}JL^%IlN(@J1P+HBIvg28~3DL8jwp9M3JDy(eYD*~Gyo!hcG!`TNP zd6r2tl!l0W%n>6;P$Aq9fQ6_h(XZqbs8matjhQ668xd4o8I;&xcWK`Fef8+_B%Ci5 z=$B|r91A~m!>|CCb|L@*J*^UDOzoe!sd~=o|NF3H;D7|oXw-Xe8+eV#ieF&J(NOs( zSM4NmY7De#y@ZXtTGDbrgGpfi6E2Be)|d{H@gIDxd!Ss@z#lEQy|)Mq|^e zrSIR!&;MMFk0RM!QV=l)|F$xPLmtoMGzNeIhP(z4DMF!O!EjH? zJ<4ZO(L~(%p?5McP7s@cka4H%MfA?c1WR%Tnp{6&;UUs_rAd}7L@Y!HFH;3kw=odu z1KJ|8)zt?pJPHfhPXuKXaRfYcT0zeGT(5FE);=LePI^`1Jbib^IzM$+^H&Qo-#YxJ z-u=zY=@K)qw`gI2DEoAjuaP3McN8qHRhPzH4-q5I#mzFCe9RIDqKj;pJzkiKd+EBk zMm33|*iUZSrIuwAR;tPk(k;w5c*%j;0|F-F%5fjl>n2jaPnOa$>}in;i-PrYZ61@9 zQ=UdynNv0Smunt;sN_`7?~!uPH#H^|RuUZ?Qow&S@$2XSDMJu41iz6Wf`G?9=wRpq z&w@g6^8^_&073yYJaWJQ5&A?0r)uJj?!C9dMH9Oee%`7se5yvfsP^Ks*y-SaD(@1x%sDE-U6RXd z#%NZ<7lP7v>c7uqeHr&1K_t9SqVpk(d<1dC|Ydr3pcf2_KFJtKV+ z4WCWzm~`p!uIZ@sjlFT!Pb@QL1{%_2jEG!0+wVJ*5?x9tkNsqtz0JxKspM&7RFiWv zVl9eW5Z$`vH}*?Vk4Ia&n3uAX^Q2}+U!Xwj?4K@E@xAY919=dPyY{i9l&4YZWsy}e zArK1>2MI02u{^_r#9<}$Y%NYu$$ao0xzbvlHi5e9I_0&+k-k@jrq3>zDxM68^ge%$ zeRv={Un_8;8137Vswt99RI{hu9T9KyX^dyp#_V=_-q97;bGzcD;#Y+;`_Jp!;n%Id zRWWvFW!xLZ5UGHFBuoGPy|4S`zd&vS(v3g%M4F^uc8Q{)YvX&DB<)%@|I=%*M$Jz1 z^CHwHjE!YnGj&PH`v#g-IiY)K(o%6*=|)dvO=>hw)}voY|6pv-lymnkDr60a;b06y z(lUBRBNRtfo%dY@G#_9dhslKsoO6xgTs?ESwd?yhDYvbz#d9h`w&J^Qj*8#}?SZ?Tpu=FetXd0ZI+}Ra)f74BtQUC00{Ww`k ze^Eu7Q|%;gq6joUB1IbkeISeRFdM)5w=$FkMC=d zGk*}(%}IW8_e1fXTq`$-&Vj;=~? zFlF}?wjr_ZEC)AW$DM%#4>2-y1RfQ7URT6mVR7^=n<&`x!q_a57 z(S+b*5|U>X4G)NPjJ%BrV)_)Bqq$R>qHyAOGcD=UxqSKlaY|1z`FH>Pi>LYPUMEnX zfJJx$c}WA)a%uy3)Un@Ng^&S0V5I?6I0R4zffsZTD6k10)oPbN%#mp_Br5R%y$j4( zTI!ONa8=WUYecgO5KpS5lO(HtUy2Gz?F`L7H_Xxo%6ajLm|$uV-I?4_u#P)f4sf)d z@{u#(jv|_@Y(2yf&MPyq0tU#TrvPdgBqDS$3V{f0Ib)C!Yo4%*Oo?6B)R$g>2gZYU zV!rE9C#&zJ8k8W!oo)!Vhy=7S*9?fpYpkl-R<#P>Khalcw`EkxJLkRZjSqL3y*gH> zi^HQ2Hdm}%g;U%{VIh%k_ql@w zNQ*M3Nd=d8mt)B1`U1-ZRtF|PJck$25+pi6{iQcW0M=EE(bD;1EEd3W1(_0IfJ7i2 z69y1CC?s2O5=gL)gLsqbP|BZ&|2{`ltq+p5gFkXe5-D{+fXXDu&W-Icgrr*tu-Zyr zx@?sYWV#M3e^BZ=l8ebXqg<(=pf0|u9KEjkSKFD zNDJK_@EJlt!vhFEfB&B?=}UfCS(o1^UUB$>3_+1lWxEKZ%}}shOxDT0CLKrxIyn@n z?<;8oPeE@m6IRrEAJ5b6^T+5dVaZFm5r*$aX(AL|!u`h;|NF3H_W%TJNYiUf8`zA^ zDt|n~#}!STQSBsoB6PE=J%$eKXiF6%zP~{E*}&3QIy*Y9b!ptxbzgpkhm181_8h9p zADj%%?@QwuYEpl;voHTzW68)ml$4_5^2X&ZRyBc5Hnfaz0m>m#l#S@~9%TxjY7{e- zP>EQl@|qX288_uJIB#Y7B7_GNabmYa;&Lq{*NCs85QkM$QaR;tpo@rht`%(q&BhF< zj3I!divXbEfwYF4k}KVut$31oK@avtA_V+LKdx{AST!C?>@Ja5Jd)>W|`;0O-dxv zO;qLE)iv54UMEf|f53!M| z0KH^Ib1UksBC(>R(ZJ)wAewMUuGhC##2Zf=oUK{-cQXCl=i~i<)Z!+lY{^`yD^Mgx zAlO(t<}ES=38{tbWB6!~ujwr~s|$uC zc`)JuKq3`G05k$3(t!yDS@71?1VPr3a`ZN>cjV}zMYCZVD13Cj*a@)EUT7)aKjEFJ|rtX0VnVmq-$IEOz#x)$eqTIKSwLc zIUCNwZtQCHI_Q7lAXKE4=65K=uKIF0yMW;S&87UmAtd;oBvoyM$a0fm@&JsU$5*pK zg;gWikXsz+P#;x}bF*S1@)35q=*Koox>_dL81Y3!_uN;@rq_hQHFd$bs7@r~%Nmej zY6V&8q(g#&9+wG6KQ!8m%v6x{Lct?dArVlLT$+)!!xV)Xr369gKq6RI@73di<>WF$9Ixh&)*PDk3?3f2tLl zD~ThX&Z&B)QLU(fRR^eaL^=jhP@@U3;Olj4Xs*Knm3jc3sHH-EAwgrSsG!hn3!Hnu z#3`Z2%v(lkO%rF%rFBS=;o8`VJSMnJOzmlboGvDOAxKuLn^{nX;$R}Ytg=i>Cn}K* zWtYtaV(R6P#DqaogoeHQ!D+Ch`PZ1Thzsnb%QT^-AXc1XEEWN+hLF_@Qb^{p33eI| zO~Ac!bW{powax2LG}h^FVCkZ{i~f~j=&xu#ZKhB?5P`6&h(g0n9j)mW-*Sv41_U4x zA}RI&LLdsN7NCF>KyYd{2G|q^i$Q|PCdfcI09+~#*_rb#Y6)_QkpKI@WW|65qHtS# zPb_$_?i)=#h}9F(!Cy_JvBFw7EOhjmJo}L(ZdeegOu(Qt8w@6ZYDXrH4#6U$s-lIW zlKlg`p5%aS4)X*hL~Q(qZkF@E+Jn)$QBoW_ybDbXhh~i;%B7l!WW|*j<5{fP4z9Rw zrq`WudqBFGj&V zqb<9wH7?QwoVOq9x=6O-kx?Llb+a+JtRp}sVbrSCX9xD#w{>AhssI3x6lGA*D4-fdX2c}K zfQfk+5a0-Sc$wb&d@RMI)@tplXaLkSHH76;V#f zNt(s-%4S2)=gC}l)*N9|Xf9|cY6qGM$`TSudOH*)>mD< zyrBLe~+H!ygNU6d7I zic=1=l%ju~cHraZB~8H{IbcIw#>Ry*cu$o+GfbHMy+kEcy?f3!D={%2iIMKJ`^PpzHpWCj)KNl}K{qNr?|oSHFMf>nE=?V4M%k8E|NF3H(}={$ zXxCF|9N>x03gbM;Zd4JQS57dqf+;jB48{(4F_8K?l4Wu`9(xd?)fW(R9KsI;X-r8L zn<6&Eh3M$1I4Oc6;TppqP*00;mrH5_Xh z!p1E{g$y;5(dTZA+nuOf7ujLem9GxyeQjBME~D2-jt|&mjT1D;X5e}GlbTGCK|95l z-LH4(wMGHtuc~HO9Y|q@t5T_&pS}49Z0fwU^?4hu2vd~@BOtk)Y{q1~vhb^e%yqjr zGq+uR9scLB86;U|l!1;=qIJ?PJR}MpLQ?<y5)BI05#A^Om^=zV258Ab(b_PC44y3YfvkoM=(IQO$)kW;^XWF#N>ni+9HrJ_ z5=3NY?;hv+FUGZhbu;Zfeywj=$nC6$8T8<;|HiftOYJtC|Xol#KkFn0=HvuX{7A~+}~4slJhSP$MV_5Bd7XPJM)EjgKb z4tM@D1?gbrmHsusxXB<+8*kfx{F0>q@Av2cj7~{2XuO4%+0DUd01AJii>{j;s2MBZJc0#o;uH}v*r;ZU8wjN%xpH@e2s^Eh*r(s<<> z)Ql5LK?=)0qE)H6quSjC_=8s%s51gqr3ui%(|jD)6?=}K9Wy0gWn60gQg3~Y>No15 zn&+M`=A*t9qE~tEvK%UFCXy!x_atHf6wPqLVRJbpjv&VuwV9Z9LM{pu3{n&ej+=;B zSi=l78Aw$#FnAnf5-bUKrE4@Q2Vn~-j63dg=`%A&M4!(*W%^Mdya7>wp|X-K06j5y zBlukQ(b;{)CYo6!EmM%|^=*a^FLK6`Cmrurwk0Nv7r6hm==aB)0uxx=8mL1-)!m{Q znYkK5`F}g}&eX{AQX)RA!_#+plU#~z3cMh$VR&bI{xl*_sq2aW2@T03%Pe+Hvq;>j zQbUho_=0|ou}#cQIne&3J(`-J2o^tFv7%FuCo?FO$xe$F0c6t5PEpK@4M{jYyP=i- zF>~Zw4h>(=QVWD{`fiD@BT}-?e&9A@K^i469Z4`-MwT2(^0_Q-tCg4!MQe-j|f7FN-{VEDgby`IFO(P zsLIrdAQX#-)V;Tlu9oi-ptSrkc=TpXOUFu3mm!%v=wj`2r_idzYQDO*nF8A6yV;Ol zCH&CQJ+-#5)u~*)MAbR*TB}#U+=ikTrd<@w3|Cr~_3i4$#PmX9UVi2PWDGqo?m=_+ z`EF~qmQSw_9z3FqfTOP0LlPKMa9OYmL4n+x^iU)+?{&YIc9M2_U4EKdFh2B8TmSp8 zWcL6?S!q+7i5Ov;C0Bc4(@7h|n3b!ro->P?0YcoXtCR53j>^IyQ3h1(nTx$E3Y zQ!QQ=kd)H7c3Gu*0xfld^);W`h3?a7A60Mn+Vz(=_HFliYU!qaDb(W`!c%EV*yGq= zr&sJwtD?;4>i=mn;T6ktT@_Y$swgWhUE{L*py+B*@g#3A_E8p zVnG0?U}zCMUWJx`)Xo$EFzc?Hb#AY;2;sKRr7KX~19OvHy1s?kSW1Y(GZdK2RZRul zY0nrD_`NR-s-}=6M{%NU#6}WUvveKLlK0(6fk?7TSoSL^CQ-<|y#2m6s?HHE%=OsX ziHOIRXJP;U(yCOXj7DdvGc19aDX~nv-{`e2yleCTWyusaxyKA06q|ih$@Uk8(K5)H zM=J;N7H<`_dEfU(w&>QD%Q@1|GsiMgMVvq;8No=2O3M_329|A@Q)P9e!;^Bsw~G{^ zRgwpUrM~F6lZG9;I`*uMPPhAD* z*V9gKu)9AAC7Amrq?3Eg?q(Oet@K`QSIfw02;P6oPo2C~bdy~-3Q6Zsl-lu%~yyiHWE0ts3F z`>7oV2|G{s%>2CT3^)68q16D01h?vhIw68Wk-7Cd;uT|C- zzD=wQy9F&dEMA^eWtgM3+@EU2W!lL9dRFzqu7-gb;6&A~0A2_LC>onsCFMeM>y#ue z5u!rrk)P6@b|U0ub(T=oawEcDxv!m4e}rX47C$KBJz8N1G}z z)~qsJ$kx5~G(`syM~K&MaEims6g=E?KMa?4gB>F1dsN$F4&sNb75g&WtTV;GesJa* zNXR=fccNV6gfLuqiqhmEvlY$1#r@y>cOIWt-_FUWHgr+)wKIm}tB zQt#RyxJAy`ad?``O{*%oaDHp92$_zk=9Oj*#?>q_4U==mNz!dr5i9r35vzsTbvTrU zxc~d0Wc7fA(`VMxY#e%k%?kZ7BcBpEqgw5y5aHV*Y$)`OJ&JIR;z8r_q^A_8GlA9B zTWbJ^e7c2%s|OnASuVJH4zoZynvEkaq-%3OU$^Nlq7jcI`@>@w?J5BTq*=GN;9yWJ z{KezO(hOjwEkS}t90&poRE7j5&(J2#SRy|&#w!EO#H55l^wm5FN47}elkU{L#|UM~ zTI*lJ`dPJ6^eS17)wL#Te`bwaBTIdj{?t>>Luel!jiH5gsfqj0i-kG0C|IO27>xGO zp?aIgYW2;j6REjn6(U|TWuk^tB3>4-@}<*lKQ5Sl&V+0L00Ut4|LwiXQjM9a8ePsw z?Y8{-VW|9XG|$zx21{E1;UUW0)AZ0etx_rX1$~x5ZaPwr>!@+sT-RH+R`JVe@TY6g z*Xh{8@)e&feN%X4&(rmZF-a!2Z5tEY=ft*cOl;e>?POv*6FU=6Jh786zxVs^i+$1i zrl0QGtE;POt-9zO50x6A8LMkBbD72?epAoLT-d_kcEv(yFapU8e5XMa$aQ(I6cFUm zDnd;#<;zxK$qrx!Qxo65xQvVNN`K&8x9 zasL!OBH0Q$_H<%G&&j+})z8l3ow(aE&RjUy{=^8rZh5Kio-=jFl+Pxo3;b}dj>Kn! z#|$b<4v`~Lbt}6FYH>&(GV_3+cPZ`_q;Pa7>&nzfMROje7|wcX_xmK(z0u^jig(QW z*OYQ*yl^KpGcN#goQ1CE^+zuv!q2f9IhW!=NE8!X^!<>)DS8^Q;fOWsvFz%M;9fZiX_bu`*mZgn&g)rP71GrTSk;C&r$|F`3QA}#uRfU)%X}7kc9EG zXJb=y$jDn}xqVi)`3%Tvy?MmjW&Motqk#~K{(&cA^-BrG+Z>5AU$drG6=pw-cvwpd z!!G!oKP3eQwRRlO9wP|TJYAFXVYC9JNIEZ3XX^580_Ry)ohilGEGJ~4H|6KlThj`I z(ID|n-~FHxX4F#eS*;8jrvj@q$Qqut&w&`ABxXS`g^o|mju|7>T9({#>XoCGB<`3S zKA;@%GPFxv{ckGMJsL}W9&FaC!26MSu*3D*d*lqarnCEQ1-4%t47C}m0T#qyK(%q9 z$8xbDLo^WUz54@|nwU&f$HgZp?Zo_fK?WhQ9Gw6j=y>!1>;i zIp#@Blcp`f(e}eMS0mM_scJ{XQpBz$1gBTYr)C_oB#f^<+P8mhJ~Uc@oyyXZS;uOs zK<964&VShRm98-|TRF3&*K(D|Lu7%7Ls^T`m9KZSoNKl&zeC~|k}}3;eXNZ$r$LhH zMOb3glmUUfR8Qn`Tbp|_-hMcukv;G@DI78?jl-Kbykej5?&F{%w1BIYcYVI9=9?bU zx~VJhZ`TZfg|;KWs_*xi^w|__A_MY^em>PcUe)y>$kB)$wVct0ph81TuJ5l7v3Le0 zrM*@~ylh3*J7LQ2&m}!DWw`CdO_iki?TdsENjp z@Go)v3!J_mFB%Y_9xX%T-$*IL@{r`E7e*B%h)-_Qx+wJzltm)P+?(ET&@~i#^3~$@ z-#t1wRA+S}?Chx3mz{XfVJv!*COPh@Am21Y1g|=0xv}JC+IVM z>VoYDP6ahIpupa>5iC&7a&^K6$wXxY4nB|)nUEZn9$F4MPim;!0_Y$Gb{)dMxaI$S z#60MPQgOCQYnxJg2-o#6$5IJRtFcR*L*+enko!#2)uqNOv56_E3G{ZkVl~W};n{XV zcv-Pw5W|@BfjJP$nxQ(Uw?q}?Nqrjpf!IPZZaixb}L$ZutykLL_v3+6^ zJ3|FkKlvZ_tOA068mWJUExbZ#zWko>AIi^ZnqQW1@~7#{98qZ7qTsU5vwzEti$X?>_owW{4naE|6<;!t~D ztCQ;lCX;qt!ItV$J1kj<(Y0z9TY>0CZhP%C)9y|cua5LITQ0Vy6-?tP074oY51k_Q z{t%uqPdFXF(_umaRD8R)^i)W8?;1i%t>6*ynF-g$iKy=*IjwL#eqT;KE z1}gr`D#WeuTbLSR9@Xz05>nWU)wnJe72>V_%xD2R+k3$%kD2MD>RX5&V`CKHcQ^NiODwnUe))Z`w1ycZ)@TBxV zfN*|~pVasoQ0NF{c4_FEuF`~~;DM*ek?P!14TDP^ri+dV=am~4F@kwDLC5Qn=UZ)u z!PGzaKP|^~=BlYUx&2$*MaHp=758}%^tyJ*Cd5^O|4nOTZ~0xCgh3-l)kmJ_yX6FW zAAg>^>B-)!C)0DZ7p(VQ*|yqR640m(L$+=FMQ)JFobNu6WHAgQ%>3R?sR<>vnZqR& z2{tUTYwDFH+zHFNL2&nIw*t1X00l`*zXqToDe0imG9?k2NI=1QrPLHuz&cR6bqf~H z(tbEbKm8AT{`m)kES486@ipwH|HB?{VsF$ekq>6QmYYn`X z8lo#LCaZ`Y1MisaS(5{QxW+X`tX~xK4ejp^mAw+x^d3DmfWPaV3rGL}gdp#lMxP`W z2YUOp9Y)3%bPa!1fFj3K0s(ulwa>772FCpB)D%RZhqZ!B)AxPrFbc6A!aSB!AWHN#6TaMi6Y=1 zF3l-#yZM>Y%L-M(`MB+QV7HRDO(*R#u+r~1jjt)=v2Ds~`^Q!r<n zD(}nxEa4Oo>?OTy?IG;t1y4J{Ke(hUOr=}HJZt&TczSpHUA4*5Zuf)AJyRI{+o*F# zx5GgM(1UYYnD@1;I7@_hE0pU7FEtFFpY+vUUtvnK|qlw?BCwZqk0JW z%xJF=J!JpQx$Pm~F4N0hYv7ko!#?}(P#*%rKk-MN}@#(*+ zr+dMmss>2l3A~44jzWb(6H&a2)y&)a&qqg#N4K}~Gy2yH-j*}|F@v4)l=rxuH@S^# z`r=c1f2(QV-<2bXycBE%D$=~@BqZh3xcp4aQaErz@!Z(B*JU`wP;$6|+g4bnT^1UB zY_QSaC%q^zh8o8$pu)FR31rQ!*B$>fd@oYf(}ucbx#Mx~39zi7R+>_v*9IiMfSZ=1 z#JSLU@<&HRHNexX=FGTqJKN-prJj18`j#EO+L?qX9Jpq)O=@vqz!hmp_b%N*+UXyiKi#82K+?`~d)o`%iCOD`g4TwW*65 z{n8^WOQxn4HoXWt=YH<98S+)V6O$3)uI?anB8 zvh|9BNdh%H>MzB1@>qnasdGxZzC0pqgnZ$RrzyM+<%}$61|BOKPJhMJZ$;=^nz2@tfW+DNRxUDP zKOB)z)kB>d;V8-#YmRZG5VbqpoP6n%j(h?58{|}}kU(3tbTKeIGx`x~5^Y#P*6U_q z=jz2n>3`UB79OlDs(KcUx9&mD+bs~J4-5zc$^Wvg*r2X#zKdn|y%8ziQDvp?;3erd zBo<_ID|R5!`*e0q~fU{4#1`4ewrZS8?{M#vXND2@c}j{pFGC3N*XNI)U7 zh!;7S(5N#fn5qgnP(yYNpP~5I!&s&ftH_5dXHVBA!e`*%l=^{<;Nwr{R{M@^56do- z-&4*Y*<ovv0ogNRFB8~mw0eO;3(;@|D=gL(J~YsI8^>_9QnV8+#5WE|h@ za!6{cUtfaFVygfsa?>SS+duFL(Vf#Z#9)?gWPBax{!o~r$6NyOVE#BRV!+0=k}qLU z=6_HyYo=GhL$`>LUj-B^^Oc2%F1Y(Ugv0mNHwgNp3Ql}_+APouZA%3zTzl@Ojk!ZR z8T^}Ij;kpm#NN%zj7rn?$qIMV?14Olp{MF8$1zhSGl(ar8*MCHKux*?zUCFn@@g69 zj1C^gWVg&3Ed+~-ippKzEcydizcXJ~mXVdE7PL0;94dRCiKNV4vO{Yw+T}JYUwt-) z3r{=FFX@o~o{wL?S@70uu5I!x&i{Yp^Fuwx%Ocfd^i1ON@(BR&CnbZ!)uc~UiG)lS zgzrdywo8{|52)uB#sLsF#87);Qw2pt#^YhHLh=L>6Jn#nR0Q^!q*Dj}frOP~1{3R# zMu-LkAaK)8gn>t}4U4cf7a5X6DG z4zEG%7cf}dF&QuHr$|q1B`3vnA$ShPo1Jp-8G8dElc2`x?po^V_OT*L``|q2N>hiXoNvDU`sHbgo z2wNOiZg;r_HM-qtzR%vL&yRdti0L0WgPyMM&MPbD7iqXvgr8Dhp*E22N2|8ZIaY`X z)WnDJh*6TqOgZUs?(b=pe-_-BW=NEB%Ju1)!8Do#3WH+`h*k+9{C~GdPn53gj>LL( z#I&cy~}pdPVm2y zvQF+-cr*+#@rrCn*vq0tU|G;YBVykWk%dT%!J&enAjA%CKpYT?9FR8kkydK4heeBC z7YvYPw2!-)t4pp0JYB%7t@OMaOTrUdwQfQBxM>dZj-kP5!x=RY5ISTGPgx|3z!Bcr z9XftMMYNNS7ulopzCT!$G!z~~$;1(U2lo8p?XB@-ZO}?b;OcpDveDr+-$VM3jWs}S z^HoARRvETj4BS?OY!uJerst|S{)JAQz$PIRcf1bENzaCGIhFPDyK_uCGu40o$T=W* z*h2Nn)&SO7R%`k=9}JWbrC3YsWyQp9*?jw5&fEsmW#It}hC40^2+ZK;zf!vW023RyQhEmQjPE4n}pV-M`11p>SMT4Cl_bA zTYDpe=ohs?u5GdG@cN*pB<&X*DlZB};*u7Q)D884Kc6S zIryj7I(4WraMKvH%Re+qMK`oIznF#HLI412#$!Wa#h=~9)Fs67l(r60w#*4@E`8@| z_)v{Cdb71zJu{VNYqN-ObH%O@Txe)vvW*O{miYhr}=jsQ${C zX9Tkw(-W8t@KS)xM&_#2EBy{a7)lbe6mojZXGRwMF3LM@Z`qhWEVyLM^ODKtsNp4h z8k$8zN{@&#p->$S0tyCq!JM(+{0Ex8PT=~!Kl%QsIZbefl?(d*o9$lwV9h3TPZ}qh z;6#iP4IV1ZSJ2j}m15*L{>F{PoZ3CO&4@ywLVWV+!t>CXJSCd5{t&^FkCrO6#}Ma$J@?4K|a5qsm*pTA!_cb)RkD?xdDpHs{?*c7fe+{Wwl#PM3Hk zlM#=4JjBJ_KKRBfY-6E>4a9FT#I%f##SH;ojWQJxp}@7%vdS>IW$m)8(S-l}k*Dww zJoAOKP{MS7d3YbgpeU3OIOSSqd&^~pQ`cZ5f$%!zJv_p|zXH=v9kEwliki;Xv15?G;4^CajgdF*e@9n}2> zAgpL*2DAWTHt-6L5|B`8N?{dPd~%#DD@Zg!Wi7WVBWsnViI1 zqSf*ub&du74l2r+vD^@LIG(>&KUqV*<&ZIv_y3#vt^G^ymsx`}7+*~kK*i`rZ#(Uz(_7AXi^tZr^b<{ED+5YAKm$|Z z9kHYiBAGl+6*+YPTGVpWXEnnQV?;R?!*vv$PhD2($88oNT1U)0Pzc z9WAz9Z9Q`MN2bV=A=;F5NU0-aKO;cOq{(Ai#D`t4#tJYT@ne$8Lm0Fh(>p(5E?c)c=m3i)bJ-iTIBERnY~*XLd3Suf~ial0(% z=hWnRbmN~CzZ!5TmzH+iD*YZ&Z(JdeD*zlGD990$tOWtAnx+xu(D*x51RTe<0g^&A zBLndJg1|pmShvgOdAn}X3Em40WFS?yv4kkUSExYLMxF^d|Q#pJJoi}b=_oJNpkwZRU>2e zF?B|wsnvNb9UcK%qv(3~tJvC6Z%SICJ1J4~oG)&&fCXUNN^`tSWH`trj?JPfqC@tL zq+<8ML>;BkLc+r1B3g*{hZIXbxz+}8j71m>)b(BV_`k!XEwgf2t5{8)asskXoL9uf=68aRE1ZoWr-|7wtCT@roPV zf37rASMfeLnk(z!bbdn)41!oC!{K-1F?G*V-OEIB+|Eoer_ody$Xraqb&p#34}0$Y zA-BG;CmP?%kpZ`cf1nE`p-8o+-oBu>*_!=6<|k;sB6u8S>gFt2QP7 zuTiwhD=MIBQ;` z$@G~}LhI3D>wVp8y;1=H;JSNXxO2b#Vg2TRYR~T&)izJ~chV{dDOj+Y`EI(%#t^X( zb|fCo3uHaT9pfULCfHE#2cwBT-k_&fl(S-<6I_NEL`Dqv&?WJ&x%?p61t?pANI zA0C3FykH&2w(dQRoAX5+L%ohstYvm9*l4o6yNmhBQ{D4d+;+XgV@ZwFgIQd7m3e`_ zD#;WZI{|fmK|ae}F ztaeb}=l)ZE)<5Oh?5Sj#<}oP)E@sG8CtJIJr1=Y)P@C#HiRT>xw$3 z{@7Y%qPg2ktOGa8Oi`b0%mn9vjw;Fx&r`o}gD;0!0F%wOx9+{VFwKNhB^as>+qrxb zcxbJ$-v_SLtrds)&*DGf&7tm@`j@0LUmsQTQV;;be=#s5fG1bDBRLcbWftl9!7rOU)*We;b=tlxt!-a`@okNV{VYmj?(Zy|ce-H$MJ<6{!ML3RJz0kaXNDHlJ*sBiTOvMi?E9#LPGK)iT;N@ zcj3Y78vpq{l_3^hK89p*K(GYmnqo)87M4?&TZH%0a+Bp$F*_?yd(T8O>s;-5aqjBj zg2th@Z7edbn=DMvyb{t^G?k-6OW9-kkjjfVNWLjsk!yy%%GD=XVc zRR?-Uck0nB9sc3j%8DJIZ9DV^F;{|#9@E|v8hr{53IR1Bvhn20=QlCVc7AqU4!Gfn z(7*&!BWYZ4YUB9V(HgN;9%BXWd&>DG^zyG+fgw@cHwp*h-Kh3kIb1hSCV!eS%uK3f zmxqGnTb8-l4%zrKy^c52b&ueO789F#h;`2+!@lUT8_u1_o;_r##e(qhA?$`--6s6X z;_kMbpt74hSu(##o91vaAFqcrL3_XFY(W#&&)pQ(xMlU#H-fPxkSnqoY@FK);KW;! zO$+JdG9giUDEr*>IHNDVz)@KR$tZqLIGveN8`&V(g7Cc8v;BaO- zc{PL#S_yTqICQCEatAY6W-mSoC++cA<72)aMQ>`)g_czuM!|Rur!gOejNs??|NNd> z@rBnYLhRu4N`1q9do)!B*;;YbfH+uTlwLe>Bb8@FY`aF-be7U*b8xWqBsJ z1$v(rXI-v3Y)n2@Xz>aqOcDKfys>+PG}Snw{-poG78=Omg0obK?11TN3W2nA^Y}ix z%aed#M;UxKlkL)*`|3=30U1%FctT4hwXH_Qevu_P87qnFrZ6%)k{914)#QI6T4^_3><^8g?j8BD;V!e?p*1el0))~!?0ecO_Gc$hYu zw!3`=*VuRstsXw&92+`K8wjppV^V=ei)RA!6Y#o^!V>9$N~jJ)X1rxF8(6uwO>5&1 zpMId9u_VdcPW_R;mfACt0p7`!g~F@F;%TDG#?Z<$BX4Q;LO?YY-b*Da^raz~7VJTL zIm8579{QRbKwNBN14WxZm|Q+tK4NNp$H`MCGr)O*(_`QGW}rc*xhctAImaARN?JvIO_{q$Fi@_E7C+y*r{Sv_PZu7?n4AWjsQ~&}JH}BA_`z==pM8pKWv62XB zIzDF1K%a0y^GLm9ltm5Yu`F4-^eo;Tqh&scyRn0_XZ|0-Kkc|pQX@Ah!n1YlvH9&9 z^qaRpw*-9D!caEiO2H-z4i+iKGFijh0}9l-_|Y1{$=^zQJIK-=%Lqc^G!fp~#ZDQ+ zu(VYWF!WwS?Y9{y@-i4oJS$VkVb3kZ(HOlJs3k+pa# zN%Mhwg@t=xnl63$CLTQw{pV^Ek5ylie|sS*oZj7eN-1td+m@*tAz~#-+nOa?GRaJ( z5}wGjUD!=;Et}4>MC-0g)4p4lE@^rd#nV-C^JC?G+#XD3C5*25rj|lqCKi?J^ASUa zeo86DS~bU|Y??G-z+FfS z0kLuya)>y(dB_>Lj*F(c4 zg%wW~aK4Db{VlucvG%<+Fp6)3R0u*uf+;EncG~L)IK=!f4?A{n0t9YhB_kg;N_sLV z$ltAvASyez^t(n~ohP@v=(*&HuLi}1{Nj#Zjol+2i`J7Cd~2s!*HJ=i_6I{5R9C3t zBhRVB3!W>&@(v9iPLW3J3aHqqf|Y$Kq?5k0@?K<@@jP#SMtx?py~VDMlf<(b3bU43 zY$Zx5e{knya(%8Z_-5k|E}^uzbF@*KQ~^s}2fz^dJ z6M2kJRQ?(WWFV$zABDqhHFo)s7n0+U=sf1@X48q>KtTE(CComqoK1_)0D+r-*+*-t z)K3wX8dkUJs%9qPyIwZXiBj8(6_G1(!nE^HDh6UD+aW_8k=#Hk^Re7!KIL$oaJVcr zDQ1vq(*OVv)oTk&vqs470wVh;fZv0 z*3CX!3k7Y3vq6hZcJLE{+#TXq8cy^ZGjKAm_g_r^#WvNLUTya@Wm?LHSYF!J9sAKS zs#(0#uH3xnPH7NE51Hc(Tl>A=M8O4~zYS)s{k?sr`*DrYx4?Fh^|u zos$fctjNJyEjYuD)xhKM#dP>$Kxj?F0;aBKW3RSAvU;2DzDkXwaNM%#`*0|Ed9u0- zN(YXOc9ulkt?j{fu9%IFp~VAEW;sYqAF{p02;W|=OYoSelcp}`mT5JzMAa$lfIk;C z0x$w6Q8r*q!lMKL1BbGF{nVE6fDj60V9e5;)~}#Uv}2lXLaDj{t-pYDI*6%`e;CJsKNJ-S%D zM=xl63cr`ip3+3VEC@|?`lE$Z5^NS}b%q@wz+hl#1QJ0A$1eqtMWY1gFiw*wK#F&) z`kCNmtJ$`^H@;_6Q7~6Vd~XMFX|c4BM=RU&!%1$LA$n0Nc*pg9#= zUXGv2&}wIZ&^w1$_}w&g%pUSz(p1%55y9rv6`CMBTim<4yBB>oUSB8ZZu0qj_QvnS z|7%7VRrVK_)FEWM%o#3=f!0_q-Q28YtoyuuslN`2m4?!alP`o!M}O* zr&#z)K-^vk5X5JDZ<8+_U~nH0trZ$foPN%3;6M^0O4?_jBp}=G50*cuQi=&q%&PO5 z1hG|0pE}l0(YaFKInVN%X0Y<|scIDZ`+GDlo{SxAYe*p`bx5Y+UcPQfz%IrfxOzY~ zr_~q_N&!8ie3PI!S(CMA1F2^?#Y@KQ43St6r1~;TWn$d(FKlIsD?jX7Mi_%dqig!h zCU2+H>z%mAtQ|0t`DbG>7(;M4Y9i_9Qw1~Q3%`@juxhfs^qKA=Y`p0NuHJoENhOcT2ir>3Okb2%ZlapR*QAy zt;K8XQ{`w$&y;qxntQ4$K3J^xT}oKIjh1Fut}6#yYP79SGaQE*xOS%61HjXsA8ToH z*H5>bdx}6<%QRl{UzIdJ-}E@RuEG*pdN115N3QGaW$e+X-8=239dQZ>L_`w?u=8ig zl9LPo;H<9q3CKdy<~qe74xX|3nVzhf)sS$`R2MMm-$kiR8E8 zj2(@UsfrsMg@t!>0|r%QidB+`(A}LMIpTwC?s*lmIa684Evu#55D)R+M5%mwa5Pu< za;xxcQy-p9I&#OOQfg{4QhA~?mybV+j^97I1gE;MI=pG^o^89M%WLXvL^H>8<`N!X zE^H^i{s(d}HknYeB<^f^NFn0)BkrleUoV=`e?yqBC*ICX!~nSda3EG8GRRP40g*or zLts#HkYGaMED<8YfdK)`#A1$=BIsn#O0Z4^$WS4=W=9_3D5>E5KjVSN8sGtFF-pOx z=!OuyhZ*LJmy^QDgRJbIK6%T_Y(Hu*#j+U8IcoUky}8I{#b=<&;%u>Xa!c)O*X`~7IPKA}cfUKguPox|W_>h{JIMsL_^?3bU>9ILX z)?&MvCS$t*0{Q(fw`Vxfgr3L^uZpx;cER+&v9$*5SZ%*z)Ti?|F14|*>l-m1uLuss z+Q5MlX*g9ky#y_ud0|%>Ke?od=rJedAEh@~A^(hhC@3<{K~Y#CA%BLDbTd2vgfSF2 zuz~MFSnGSojsU(Wu2GHNw3;9-aR8(#wFWHi*UOAG;E#xyw!vj z1A5;;@BH67y)haSr--sUbxfi^?f!4{3ck03?1{&X=BTmiwl4q24DZOR)k6A zyAk9c(W`IF+>kaYf$+6EoW!o%Y1cwtHkX z7fPBZxaZ_Ky@4hAiGT)|*^Cse;=GgzgWzG{atmHzUfT4|W<(s;Ju0b7yw2g8iO4~& z0EP~lN)r1G4)?6^eIa#j`MWX~GI?45+lNm8S3N0^ z-snNl4&)w0+D7xg@%Li|r&m@nSaSHmLhsrkA}N86ue||4Qh&Wzk#Uv$i2=~-MuS;S zM^e|xk)EDir_{&_Fv$Lji{)TY@&m;2+{Dat%E1ZP8kQko$zY@V-{+Wd8n&4oa5etU z0bqrhh}7}tDdksL%A1Np%{Yy;5;73vyXwvSsLd8_q(9?2nC?@rzLyLYxMKfH6zWq# z<|ZE~o6^w3QdpQT?x%uGP2#rDLb4nGF|NjMZ3QL?(Hz^x>=4P%tX7%NsNKORElN&J zN@s$W_)T%ri5?Hz09OzWDjk5D=$Nt4NCwLsztkuMnOkSmW8c7k47w&pZt2a>ZcsSt zt;-J<<^}D2^T{CaY~K{5OGg4{X4YsDu|`(0nzGC3`XW9YjvuuD<8$IF)FzK&=bVNK zmxc{Er(bWqX)vAAL#%SMT^&aT8VfR=^H7`Z`dfVLcKlVALT2Sf9@>1&g!VXibU*gXb{QW$&x_r*Fr9RRM6?O;rWj-cEUWbSZV|KX3n<^yqlBrTWJ zkzZR1Ft8+q0B{WEuIu@g3r=-O z(lONBGnEWN!d(@pDC_0a*agqF`J3n=tvfKbu*}QMg;EA+_D$1aYTeUSS6=G62rnga z1~$*|1t8_2n#N+1!28#~DhN&77@T99Tn8E7$RGlA3B9}P!R5QtG^^Z)74r~n`vO*OsgMH|y=CW*+t{Q)6bTAvG`?a^>ozYQ1G zx3zRB7xhu2+Yr)dNobUBAv8g++an~a#IyU!P+JlgsYUMHMCNVI0Q|S316- z<7+0lY*%c!h;Gwx-(K*sGnNizrtf{U5r^uUc5(j{HOf#{Y}GiQ;y@q(&WsGq z<6og@S9U&YI!9y;6V=2-*$*loUjM-6j}yG3R9Zq*q?d;&7JzCk|j*vrG-LGQj{l(`Y9nWWM#!% zau_1`(f#xKtAq$xs=6^`Cs|c zB4-d#Z3aS=o79N;>G?`*#(v6<6%#4WBNh7XxK@Z^2FrUs*doSk^oZpV4jIAbXslVK z8o3$ljo63c{5}u0e#IE+Lob?d|4Aow`a_PHt-R7OG=?G35s`%Zp|zA~*N`{uK{V4j zEEd?>>Cq_6lsG=_%x5=UjSJxux|Ds-&^!Sd@>PxrRE8Vjz#@DnzGrNldt91oC?AwO zUHz>*wRmTG;WTel?M$30uEVkq!r*071;FUE9}N8oqa;<*UMd_f;5o>{=(JqR|M!+q z6m0FVsV`XVup5E!YoxJWe19&eMpq04IN;29mY3N|v`#mXqM~tPcNI_pc0UzYUbdv{ zZTg=>1Pw*^3A z6@j^yBvD^Rt{}Bv{XN4k=$qCcv_7NBpK_*&Y^t;o(&@-<4Ty`FO|kmSBW)+r(b7QX zqY`gA%B8JuET$Mh(UtO z*_WR-E=Q*L!5$I~`qO;GX_IN(?J;45Op4!O$);qn+=AGfdaIlyluTW(Y>$IFsAq1f zQd&RR)dIDCNaFBJE5p}6It*S`7Iht-h^dHce!qR{%^L^ggKm!yP){t)4sln_Mk0;} zf)OJX{wxexwN-`-2RCMt zsF9@t$yJ*aEi-1uoOW7`*3H*4geq@`B{&=Et#m+mx_X=?hR_)54nfc174sqE65q3z z*bk~KMyKTT7q;{re!u0%#4cCu*F7t5WwxA<{lmLjQGeU_8z!5~|0;MzHo{ks#%DWq z7k78s?0Kow)eu~Olxe^~fm&%Hc^>d={-X+-%OLk9H2)w%A#rZ^J5_Z1PNMB*zI8NE z+Sws%i!puq?~>ZY%NPpn6Y(exU8$vm<7<}1}oDsyaNU4`_)x&H1?Jt;q;A33*jyLc1oAS)EfK>PK6 zYA{d;W(k0JHxM#0aim<)pZW^<6dO+V9|8x99NFp~O7}$R6;?`_V?l2;ygZsA>@MUG zxI`u~rlFydW4z!ubwx@Q_#^EYGzmNcvblnDxqo&#&b)6s1nA-mCSs%f7D2&aVQ`U~ zKWll7d1$)}NPr1u#D7bLwyNn@XX$&b4n{ z$p8TSXlU8kHsm^nf3n8LlqXe75hXfWondAKturtcpFb#bDdJGeV`YpU_1W)rIvtv$ zE`O+gv@G>6!R-UHS=~O-w!{f&tBfb;3o>L?pj0BFMi;;O;^O_>>>gk68gV;rYoH(N zEu**ASg~3CH7kEGcav05{B^?HZ??keb7E6L$yC$^DsKC1WT zqoxVIfmge(v94vd@um{Hvf;e3APO5W2$St?dn;Le&Jp;z z(20?|{!DaKAET7(unP_V{4M9fk07uA^M6H*YyOb!#tUbjLQWwFFJF4VvC08Oy65$d zyMHRJT#~-a5wl6%sJjtT5&Nz6K=FEdfBsc>Ape08l7xK4(->P$SJo0_VK~`FBtRE% zD(FFcLi;aOl(Ps!7*!QFs8@~5MGRV{q7s5}$dUp*hjrigny7`t#`HSDqPX^(2n zqbxUKVbmvJY+ROGxbmk{DBY#$|NXq_%!TAq`onFfSm|~6P_V#I zGk5DFeT~{ylazaURP@e7d92SWxEVP=d4grBcG#Rn(_vM=Fh17kPDbKKmX`TE?Xaot z4-l)LWisDbzpIG2MbvLT9jnrwT9;(5g;y`Z8T{@oYiu<95~9ynh0Tae0app313^cX zwUgr{!5k7A&@;D8G|%&RF+VO8-Ue13+r>EWc_QPo$lE&jj+ zQL=a5N!fR>Q+S$0dD#;6S(#ET-qHmUW#Os7Ie;phR>NK5xu3uO4fa%72o28|8`pn!$_yrA zrl#3*agpGTT~SJ&DHvERNL(hz-(mMV5JuAVQbaUPY>s`!6AU(x1jeQ|L{4Fg+kU_=u3|Izf70dX}=vx~b!kl^m_?(XjH z8r&rWcX#*2-3bH@i@QtE;1De6Uh=)qJ^!Zn&u-0}-kIu}F6-h#UC$hfj8gGtmSD)O4E}Isc@@KJCo%r?fod{MVM=@}zbj4;%o1RHch38Y@(^$nCRh z);%2l2@^vF_{uwEY5nJu7R&l>hhD_bt}GloSQk%PkB1h%=G&9XIsftB2z)dMazk$W5jXgm|tI}-?Yn-rgxAs zK{*GlM9`!g+h%e>_16gqX-UL^J2rwc|5kG0j5M*#y!Dr}g0>3f-2mk^3A$%f8AZn{ zHx2Bzg8;Qk)20>PK{%6G9NsL+E*_+gacm(NO9Lc}9LbwLmPm5B@Dp@$v#_wR;}U2n zT6Mz!>!A?rpdlo05)bjBh?~TooBb@xwWygj86D&+{j31?ebDq%4< zxgL#;zG>8M3u}IDTtw|SHl@{t4Qo$q3MCsHb6HdZ(OQ+nPG*2kIh zeIK1AF|yyjqMH$tKD+Q*K}%h!Li8CGbOk1pvO&dNsNpA(mQE*K+kCNXg_h*UQmnW@ zXc=;D`YDIMSnE(_+f0F=z&v?7x9=F4`WqcT@S?BnSj|&keYyn`Dv#;R|BCFEjf?+i z!qKiX&KcROzXCjZTNfa{MQ+yM``GT8d#u;LT~l+0?*37o__x)b3W46RT|83}UyW2E z$Y&ke$I7QMXsZk%_u#F5NNI%T?dad)>DzAKrk=|&9@?vA%N0z^q>@jBg@NRzWnu`n zs_d)}0f@_1^Hn%b_49z@iFyUlv`a%(zt-uh>6w zw)GGS6-CB{FeT2d)du>y4!6NeMP6g`yQD~QMrk>4>Lu8-ZWzup5##qOe@3Y&Je_Vi zuI$6*a44zHn`&5p;AXfa|7*ce;b@e2WH!|mF}wuoa0v><(QamdtJfW zs9s(V$^k&ZJ3YMc@hP-O&YEIHlp-VI^56X2Df<$oAB6Xt7+suQ4aOqN*7?KDTGDOy zOVOa%H@J{ETQq9sqgW!=FjTQ2!k_4&MRUVf6&y(kV^2-ZDSEHyhj!^TGpA#YF--{I??3g_5OC`UPL zg1jkwpc)_2k9uw!qTlVO9BIA5roV+IT2wH)SlVZE0tClTnlDZs5coQXX3{x%zd;vf zl{A6m5fQw6)b-wt&vSAVC-yuZU53=)>nh^tZ{oS{ z8f0E16%{^dBt2H98Kk9Ap}p($LSMr)II-#@nRo^mzGida8b?8ee3IZ69f|xh3>B&W zQ_n+R0a^@Ux#*l|%SYSMr#b+%`>+!jD->{S%)%Tak?81I_w#r0YHx=-W(@6H-2Ni=)j_BUd}Rky@TI_= zTerR}#J%AkTx~4;aQzCP_Jal(u1!DV^yt-0U9SfV{n#S$(Bac=hWFD2(H&b!5rRzSs3ECLF9ROIbujdIRX*<8tKBTcP<03? zETr|Q3u1M~n8y>Z{)YjfBwn@|DRAJuRTcsj@?ZcO`EKf9B$Lma{?9D6gsa?PxVMRv zPq&mfA>EoIe>YhcN+&BPTsO--y^yrk$A){gvUbIUG001MlYkNatiua*$r-~Z!CNkF zeqxmqrgdO!j~JmJ=3E+xITrWpAVwXOCj+}^#%Cb6MxnB~d?rQpL;QP;d`|4 zw~u6g?w8(8h*gXF#sZEVj}i}FNn3Xt%W7Fmu&$cd;pv}hho!m&CF8Aiy_X~sdW-lE zZHmR_?U-QD`}OpJ^Kg4kA4q?psi2?rGi0=J&@TKEj$!}*uy`*p;Zq_gj--010eKew zhtHHUh{9cQ`am0R35JD!Qk_pXPYbGReOVqz3#EU@0)53OBsS>}fFIP2VByP1vk8TV zB;S_|VS8Pm$N1my+7WKoxHk3=DheDCbA^eJfeV+Dnvd&6_%qIgcg_rHFL;gXr zQWnvYF&(&+f=>@{l+2Te=L2zJ(&b2OS!4-_lKYEWeGF z*ix*7~63PF^L+H`gRB$K#)RvOy{VAZAizlDd8BXhX$M3}M1p5oB>=_=>SQG5e4nH2SMH1rGA(-I@?xDLsn;EuA$@9|PqhrIr2H;Qn=9%ErIS)xkGeJKUAnWe6pUxpTFXZ zr(4H|GHbWfe?)OtjePDo*z zKVoV3C)=^wH7(&+_Uwo!0V4rS*?wtfCl6&~Ytl+aSsOgc;VjTnQ~Rxh^-7?~W3_x% zjtaLeGM{$y_gKg|OY{vSe>t_i&uE$7w2)-+%)>eq`bz z>An8)tTXqLuhVf1L-0|578f3>fqZ_uUjgb%9Cpg6qi2dcBD^e`Xga<@&s5yxDS<5+5%03g2ID!8;-ix9=B^E!^OXL{~~?_9uy&Hm7OF z6$ejWUu+-FZ1)WAY*q&uW?0LJP$xlTdq`CYI8?09%9>2li= z+Xg^1lO!YPEj7BvMZ4Au6MC8}J=v?3PnAx{zmQ~`KD)PU&6z8<2bok~@jkH#p&~}h zc1&NcZsaVi_dDxz7*jC^+hAs{_d-@>63$&fu0V~X8rB)@^zFv#^R0r z3Z6DY;pzp3G zkOp_11+DAE#+N~xSa*5H!i;EhJf1naolsf2JOV<>uZ%r?DD#M z*L5%RyInt1YV%R1HoqZ3Hqg1!f^;JKWR*cm1p%EMSk(}c_Okh9{G=7#KF-A%(XJ5j zazEUKoxP=}iND^$`t+t#b;-f-36K;%Mr>;)oE-}+P*ZU8>i#J95yL1~rt8aTnrVzK zhEI~v5%xI4I)n1#uw;+eY$pf8!9jW9$-QAhD2E}*JIBNf!6*zvwwH(CARiEQNxp0l zvWt>HHI|8Ji5n!I?4K`ZS_x+doo)0Prn#=0*_+v?sfs@ng4V5<`?c%5Yy<0bzQeZg zpV8Vwqkg!*s*fDiKPnX)sj>fHJwW(VWC;;%xZei z878l!I?33hySIE8SF6I`zRnA_g+fSL(vh7a5Qk2_Ev@hJ=g%msxgPJ;s^|6kv_OUy z4qFFE2?Q~zl;2PLqX%=fGEiOgY08@7XS5tJ!tk@awpk;S94JMQvK8T7cA;dn{;fl* zL`Q>)UuIYB*>L2k5APyFt)1DDnv%P+sVFf^1hHEuCsRFJG*gzxiFp-@#MR_maoF@z zEP$DYqh)C|WLD%opEYDhsG8;J<(4bDAM{V?;PmaQw&vABQ>|!!^%N?3T!OUHK+3mi z&mHwWixV4_h8MnjAc+!_awsoFb}k6$CZSL`pKgtcQNlkFf)MO9_c zzhaIgcdoe6N_`@dUtr+j2#})OH>4^j){g}Ao~c_sYBi^QDS>O&6w;lqXI>UQs6YF6 z&VL6$=UW-PFk<_7F(Elo?%iN> zBW+MOn}n88=80)xG}QC0{O1-OU#grI=pm|OA7g*shYTjBL<_8(V4FksxB0ziuOpLM zmX+97jZF3qtjbXfgFfvM!x#GO;t0F)rLG7r>f083BaVip^-R9JH(#q5+1Kwmb$a<$ zO4?JK_6BJa*F*G%ZZiyAz#dS0oo1c()HtO`-V|k|$$x)x5v-Ha<(%}#z6v%X|AiX7 z)IQf~=STr#mY?C_C(06J${*_Q4bhe2>LWeVW- z$?kbGd(`X#5D(@gutiAIwLAP%VideDWGFN2E~k`Kofi+O))eij=wfAT9aTVutBZaj z4HG`^FTp2_6E3ss+q}Ovo5r&D(-!3`*Oz^)*1IcetW#t_ch6;q%pD5fSN?>t0ezR5tHV&*-NiW$K1>g2gg3 z-|=wiBE#(5d{~$HJm9mrL~*ggM`>RsBF@^1hcB)wZZ(gCZpyrfO?0vXyP6zk06ok%9T4A0!TSjo0r9xPElzk`@(@y# zO8rY88hvDyEvI`(eGvEN&TlFutny|!lwhaU%sWJkGSVQ@S&R_VbbuEqB<;eF#r&wt z(GF~Xwg3KA9?&!fNy;WE3CJtIl3&wsvg);+zM}A}Zq0HgjMSpHlBo7Y&Z)NI9E}^t zr!TXgVlu-={9JD71myj*?u(s#Tc zFa_==wZ=iP>4b@6gp9Xt!nl=6<iWz z9bwVXq!`JA4W`B)xO8-5ix{EKhRcSrhHw0wJc(z6^XURX)J6=Xn3U8AXo4byCxj%V z5@aMzY68M0S-GOG63(*8LU-z}WedX~&yGian4%_g6vM>PmdL^qK*BM0CZ}Th0En=| z6N(6bV#JC{0KP)UjY~qNV&r2(BVZ;Ys@8q6SB&|}_}pHUY&j|>0WX`30U5;rc*mWN zNy^&On(sk(aEHG4nRG}{T-1n)Of#Tj6G5fTE+SoUp#^)-dWXe`f=v;VL^Qgk=S z{N($qnUZ?}vF5XK{QbXa0K+m^+|%2~KMN^r+{;fV=_CL5!F{-g#vw?pAGh4^fa|Sh0st!SPVYVSi z_CD2#u|=5+n3_*FLFivHo~O17|5@xV-?8V{97_BYeN36a)@$_U{=VDxb~aZJ`xrQ1 zkp0jxGxFFyRsG^(jBRM5lL43GC5!pg^D_O1ilRk3>p53(rnqzNo^bUb5|&e11;R6a z;_~JY@A4@n*~TAMcCKhXXLxn-;wjUkYM;0)2w^7gr=+I2V;fdGMSy5TlaxDD!3&GvZH0}{A0X3TTum3lGyY**qut;4;<4on3!+XtfPk;Kp+r>j-iW}CXGWdO@rUB`u(7)u4+r3 z8A_*z9!wi5m%LPN!G82GDqcEonsS;MKPY6BXiecy)OW)1*S?3pufupI`f-xofA8t( z=_P|}enDfL#GHerM)rW6CZgjcKF#dXi3_x02r5mnI5(aFmEdLE2;c(SU9e5JbuFsEgO@$<%=WEP7VoJhX?*jH9ve@g|7 zfD2LLDCx=r8|ea2?Prh9*{&37r%wmzrI;`kM!!4`5$`tBtd?uuxVrUoHYRuQbe^r{ zH(0Vd)f)PJ)IX&CUKAQ~@bn!1yWNwokF{DCEpETW8+uyKdVK`v^G0`6uoToSf ztK{u~sc#Sy*>2v!Pk#m?Bj7Q18@`L|X+u#6ME>6-?aD#g<}9i>wEU0OdSy_;8Zhz~ zi?@eGxvnUaO+WpDM1+fiJbRp%7De7xiZF2pC49l79`c{L} z8BU$2Vt|hDKZ1li1jf*2dBtuQK2eE&=e!p138q{_t33o!nA5(xU>4%}Zu>sxN`2uB zeA9pHV(;sI*16JFQ+>KT3T~L{u+h1`{uVF~Gx=SG=QCC-N4% zb^vq;3{q63wqz$lI<;wq+qSqB){jmH?KMiLyng(uKEQf?Bh-a_CJQOkn88Doyf(?f ziui9c>t1pSDX>NK(Ajb5EEek%{9%o^Qzc$1I0*MckNj`-d-r-y%Yi(rYt9b_m1+1( zG#aH#GK!~bB3(DlG>?HZ8WTa>R|X0BGmA=rt>+z~-zJDLH)$g&0ptL5b;*U_Yj#uR zzmbzHzA|)5B8Gfp_928}!@ME;0U%>F_)zCE@jyGFSHt05qeh&Ou?d!Gi$o_EU08@G ze=FqVR~4Ph`$aTMSG!N+G3SumG_-#L0p+21=Bb&poI)+dZ_7j(hoyfwW(CAIQes<&uSnTj7iILcDW6`(%xPhB2`zRQFm*2gXAPKhtl=Ac zwk6I*sy7Wf0RS-s`3*I#Z_|jU)%k5IXbACK!@rN%bX(Z4EX3Uj#W zoRZ_#&S!c@O3T(|YDLN{XvOljwk=SWXl2PQbSY}tHIylffs{saOUf~i%5NEM&9fEZ z`lluLKu+hBY{yup%huCUDXk+Oe!)UXtqV%jd_DK-l#H+pg=U>fl&gmp-T zWml4fy0jPs>}AO46V8gO_X>MfeTMEd0YgIFDQq;xBDwpl)?NZUsF~!j4+RTRy$K)D zm=lv!Y8COiz6pEUcy`|vJvJKSn>s@vy58>vYryzRPMuw-1$yiRD7v)|*P^2>6+FE7sFLfZ2TLQnqJ8Y{h{ zj`Z=k^C0)Gm9nzF2>-0U>Ib$OKI5PWjUN9Rsu@%lVVj8Nx6>s*-$~!UX6E;im%;z{ z3V&oP@%K>?yv25azro`?)&Gw^73jY69l9{uUjqP=FoX&C&l#WtX6WUZ)lV9=S*OR( zk=Sr2t-0ucgk?mX8 zpGkq@wJ6SJf#~nn^(4Vv2J%BFs3FJ>7l}()%vTw?Zm|%td%V)vDI6`BR=%(134Fbn z+${K~XuSa-SVyhByFh=%RYO7<+YP~)D|PStFcaw$U=EJBhPJ+(*vb|Z;DdR!-=d0h z0zarRcDxLxB6oz1kc=PShfk6(c35V61#LIac-NcJMz*m{eA(o2_8XDy>;3=O-9PIu zHo~gaaIW;5{1@YJEB6y{mD4Qr-BIy9dw|ifEQLVN8~DnYLQe!oNvjI67S}32w=6uf zA^meR3}dbFOT5MW8yX5Z|KpYC!H}#;n?My%Q+tB(%Z1BpYr$6HTJC6`gmIsI(>wk4 zN?S$lB%zz6(+d%2+2yuK7T3EuEhe90d8LcpTJ}j^j?0^IRDFHrv#pr|A5`wbIq%^u zF!1}s#cE#Z#Dld)R&UgxyJf~Z-@W8JQ;J{;dvlC}b4tq|Y^oWbW z#T76KZ=(Nm@UOP2&Gt%(q1PiCsJM@S)Tt=84d`0m5rpB8ZEZ_(5JdP&M8a2kHt`TCz++bh{j}J$6&bQpJ*J2 zQ#A)5Skiufz%1U!CL%zCDm5eDGp21y0@Iu>+!G2C_@GcJaaW8>^>03Gtv}P#@u&O5 zl7Gt3Os}|P9**RfTI=88+Iex+a%$WkZvd1Me#jGnL-4$)vyXLcG#+4jv)}h}&@@<8ZHn0u6*&CFc%EyWn;4Zy_}>K=|J$bzc9?d;R<1E)j@}%|Kk`N zLSVFPjs20OI@6RUW~i*z)Qxz8&l_jRaX1?ShLFaW)W~%AA~0O#E~4-q6bo-bf9SsJgJdG_nV?!BaU(!rIn~ZTXTbO8j5wyygP`-HHEeG z8@gyt+MN5BGV(_m$i|gnJ20%B2Nhq-FVpxB0}i*0)0x5xV9>tSMCzcp6m13sxYSk+U#LamW*T-O8s(C96q3*>Dp`?pGf&*>Czt%Tf)2 zc04MC==g?|By(`0)nU>#x95k^@2tj~KDCssjA3Cez<_UY^$oceMUP=fXP1*nw)e|x z8VZjBMRH%#II`4>@besOLF?EwlNW)~=o6@yKg-&vLo=d7PS z5fM+Br8_4N(ayqCz`YApJWlFY^n}&sMY?D`o9e3(gkq5BnyV>J75ap_3#@y?ki-$g za_p>5o3Aq_xSP}82rA_581!tOdrjv{=)X&(u8o|I5}&hgUW#}9NCW5<%{fa;_b4~0 zn1KDfl%V+s*l{c_1kMEqWRFU_T zF*q99b3`3FCL|-TIwNoQJJ>Cqp|4S#v7ctyA6hJaSJSE@rE*(D`C3~?d$nf}6gL#H zmgefh=>kMUqee}jmGO~Wt>crwo6I-@^Zr=M_&DCDXUy=gY7Aq2wI z;rJi;;&dMm^nG-Ai?AteEjB%rvR%@&o_F;k?M9LuNuq8l2C_FiS;Ge6QvaV1l$ z#~!Bn$#~~4=l{enCkC$f0%zYBmFrDD#4R0p8d5Gjt@XhiGA>z<&_9R?y>wGzfU0#A zj%n2~X5@k+4jBw?qcPF>33!pDx=mUW$$C2Pi5uX%NysnzcB5Qj;MZV%f0LSQzKgVI z-UL8#+N3=4oz%1WJ0_l{!VRuW%*ec518YFR+ulMP+)z}36XMEs8}<+H4U8WcJycxZ zWKJui2TS3Rzcxa*BoUhJIXNCi51m=W5c>%WdKm$W6N#5@gE^!dCB+V#6J@yiNO-_t zlM68TZ-a;c%3Jf|S0wpI`&`4MMVtA+cJ4{mUw;{jvd6D?aLG$|aiGR99xn>4phNw` z*Czbk7x`SP_VpL&Y9El^tZ6_&-B8l5-P6T`kLJ?i6|*L43r+Oj6*?{w+jsJx```*n z{;}FO0BA*9;}--*-4N#3I{ffawB&5}TFM)-E*@r&$U<4|J_D8aw|?-Si$vk~)w4Uv zd@Z+hTP+(a!t}C_9zTnmN@n7j=#apYHA;eS!ZBWpmQ(ya@u9sXIcQNMA=gLP8TF&{ z0eK-WjkkW}X<~R`{&C&$A7v|e%arnW8lNz%gk(!%yD>u7lgusqdy>WP-fz5yTYm~2 zthheAdph}hyc>YQf6x-zQbgtk#91R^>iV3c0J{*=C_o|W*oxb$%x0Q1gbOr}c6o(N zmGt!~tAaEqdSzipA8jPO#)ADT31=>$Fshp~=CbsRK{pqI(}X?c_;{VS&)DDBU3Zpl z-q|*c```08Nyj9X{o?}pw=%EYcaqZUJJ)}o; zilCRkU9S`SSXWqjxRO0KOUmNo_EgqAOCb$5flVfnmm`ZDR-PL79 zW)SVpQQ?jxBwqBruI2!I)nM|TM>cG55F`azX=YDqNGEzoz?3DzFmQ=kJBBq;)dVQ{ zP_!mlo`gV+*&6vH2=ZD?P82H9D5Ak+dY_lwk|l96_lBomBi~mvRUlv|%(l?;*Co%4 zmiZ~>M#INgeVYa4-;O;RwB^1!sO>MMIb!6Q)}FKQ17oJt!3h@oaUlpVF0u;yp0094b7#69-ba|8NPOi{57|2Pfr3F$C=Ab3f+v>8awT6EuM#diZ zJ79Io;KT47k!|{tC2yUm@Fj8IHmQb{X79w0CF`Q{Qc*=76Zu!+@>V1pJ65P>!lB{@ zeH(ry*W+{WAy;591@OZcTh)cP*eYw{)IdbS-|m^`mht?}D{6VLO~h6Kk@nZM+d{>~>_mAqgpH+u-NSgc1zLZchaJA8dzl{|(0>-#7dHaX@U;**r4#R`eV-Hv?Of6(ETbJOLBhJYIj`a&IOKsn zwl(3nak;4E(3uc2(H^^Z`b@l->GV1HE$|NfPH-69!05;{v3;2_O9k|4JaI4_B! z9Vmh*6;u#Ef(K|yO|9aOsqr~bCYx2LVKwwzTZ1lh{HAQU9u1~DR+hHhO(KI43lT69 z{C6n-ewpX8BKl73$U{xJ5%^hUp?|6IvhFL~eoMl7f$BPhKHU+cXdJ+;#r^=-F+zHYf6#xa~q_9A`O{AjAetsNWzI#mJJ zE0!dmnQX9G3U_dWp1oHR?bq)vY8)oKRyy8$OOM+Nr*@AMxZiq&6E|pswICv8tspq} zl>e=S4FD9-Yh~hNC74`HFySB1qTV!^)Ct!82N^*d>c)^FR*gDH}zHEug%}c*PaOsl`g5$ z0F5-X6%wb`xwh9WB=GdBZ~Ibz&=XL?!znm-LuTP4tEeP`g*u$yT3P4;tT*zl3mEnN=6y}KK z0>uhbvt-QO*spY4Y>+s$9eOybjB%1&ae}Kf5W%8B=ju*yJA4J-{We;XES@ z+v|ZoLBSiDLo7q%jb;f9qvH=F4A^js@(WlPe{V9vD-U`mA?#LMWSgY)VP*)!Qmn!VA~zMr(2#^udN$K z+VyvnL8Xr1%`t;IJ=i#YRBud8fwW4NKoaA=?x3=>*DbmWYuyu7QAF%4?QifeuImi# zEZ@Vo5asqUzZ;1T&e(frp?qErR<)y&&NXw+|2pAH{=Sgc8B8r?Jt%3dOtVSjOAb+*Xx_FTi zyiB=%(f)q5n%;>^2q(ldsr|KxPR^d5iCE+YL{48qqobqpe0H_&E#)D}83DjV5TzYz$@6DrE2Q7?Efk4lQ}*e$1J!sL(gx6O z`Epg%1hhCAcNTQ%e^<^?O#OL}>)y36SA@fa(;pz!YN#YG^tW>FXhC|MizPcBNI@on zca8J88FRC=@RMUD1@Z(DDYt69^qO%u&*{2w|4RPY=5}k3rGb+fYEdWvKpSF+ZsCal zB!?Ip0s!Cu)R;KtGfgj z7-kYwX}Wxr!O-9lg^fpdM2?@m%+UHzA#HCARGE zbj-8FrIMu$3C=LF;>pl99evHv{Od~HZ%2n0A3ks&GC3j!FU`4fbm5kcj8Ht*XBrK0 zS$%;Ixn!f|N(eNcqt=3=!)$;PC`YJz5F39+@shJkZij~+@4oFCXM5cy-YvS6A*O}Q zNdwxbp-j3pFh=HMy%I21XiZnI!;&KobpZ|-U50w2!Asew+M`n0O3t3u?RD` zBH4q&K82Ox0cz3di9_W-Nc<{u;Kas3k6#86`sx%G1e_JkpX6Ioq=!$Oey;e>E0*c4 zXPSw7+Hzc8qAsVy)KSXZjhHRhL<1dG>GBNbZuZRu;}PkGPn(-(B^Ip- znoD&Qi@;mawH7&|+6ztbtDMs=bg;N#38BNZM4Myj7XH~;L+~My6X-Vi^0`^F?~N3l zEc8qxdl-g};mE~1{n>ygzNgMitVZ^*`>LV*a;4wPpWJJ%kJEx<>ck{7xZ1O$qtu}Q zElu0X@A+Tu`LLf;u+>^n(AbS}&IuGM0bz12s5%L$gu=3)(mrHnam72)3#qZxBn8!b z>m9q!`F3g25Z@ZmvnH%%Y+2%Dp!9#vF^g$h( zhZ7h7t-*yCMF)s!r5y@!mG=A7&|&Ch0z|Z4h#?a~Wp%sQz7Bml!I$|(pv_&JDi>=q zoRG&p?%FJcOp6IgSaOWA&ZL?GN|wDXouJ;F{4;9WSxoz|L)4&lnmpYetkgbdWZ2wR zurOWd%UH?5sV`RLB1Hzuv60iC)NCvCKy98=KP97gtv#=!Qn7ag-fd_!1RSqFWLN!$ zE`hvk1qdE+1grv3V<}s32C^UBI**zyJnT3PONWg`^f7$30l&+2Lk^Q!E>ZDcsz?wbe#I*j?ZBdg1vzcMWx=L^Q zOA9-B>tr3L_mp~|t>$KpZo}0}(T3p88w5FAn7Z~u|BMEfMu32R zW0y7vU>NNhgrLUcKnTL8qG0WngR6Do(i11EF4A~kY|lI@Eu94nxt~PswGwTTHfil5 z{7wz*oZjk5Y+M@A6xHRlZFZVv*r)t$cwb+a7TBdd3Fh?eyP)pUZvJjMr6S%%+}585 zp+)ns&O=uIC}Jf8v|MeLimJjxXVEVYBoeexIf_FU2_ZzF$^IfmVM-@cCD=|@ld$u| z3p5SVi3y6d<#EIO1EDu^0JHhPhXBoyCS30->cTH#g{hAmjf4vxHM-$Yf)j42PfXhT zGh-r*9{6*}It1B@s*+<;0@xQvQppwF$q+ur79ZzQA&DLa7Oa_9e`OrpsQ)vU7<{%I zDhgnmoE4S(0r$E2w*cW6d_9PP`A17hNJ-RHEy-HKE_v%hySbD$F6Nz#;ZGR#^DQ^@7eoMY$wf53MEw(T% z$(QO!#b}iKSaFhjA;EKBD%%lG#}fsV z$D_dsUaA0+{EPND_hiizcSA6VmK_OHk-x*F%8=i!Xf?@YrE-lT_E*odIPdtV8Buq- zZCgL<7^vMZaGbFRgJ15s+xRQ}QcYaJY8w5v;WmOt*oMELR2;VD=fVEzrS7SS+ZpYK zyCwwhS<4yjU$+$f<|zzYd-1F6o-(8xV<@MPFT6YEf;qnow)UJI2Z7n{$HJQ2Ir@nb zB{WEnxQPMq?9r~pyEbj(tITMYc-)+f)G><5Q9)H2PC6(EP;fF-FsE;+Iyh)`7;t>L z6KG4-+?>5py4{gFAe>UquAC&Z#>}h+O3{~LFSojON|Mas^<%g<-!2&|>^+Qc4mHOV z37ArFp$7$iltXY8U5nCzDVI@!tG81YOLBTSzC+CZcb?C=Oyv^^Z>nV0%y{G&T#`yq zLeK-q0B!mCs$3OVd{(+^JbwRJsG_g4LJ&3U$-&h8nET&Ef7=zRV*lRYQh9`8UpU`s z8CkxM>e^uL3w8p-uYWiUZ)+grs`gt9Y_m9l*e!NP>laV>{Za!J%Qdi=0y#eKzb8D_ zXAI6O22REXR%CP6lKHZhtA#cE1IE})23;7L2#uRp+-FowV2 zzb}a|f!br>%_$~tkO`GbZGnyhU)sY6bBA8T_4s8 z;eG?<@Hq1v|Ju%Hz5n|3`!j4`>KC#w6SeyD^<>WoYRM8tG8CVevsjE5)VTcbe90X( zT6MO4>PjtAw`iM1N7{;RQ4=ybzeS&PvgNYe;WUyZup?MP#h0cOe6~9S4!z+a*=G?# zP}>J9A_8bwCIjfz@0bpPtWc?Hb-jMCJwh~VU&Q_(J2#JQgX0ImesHEtc6Ru^191Ti& zzjpUaaH~Y&xrM)CtLnafTO=D-TsCXRC+n0IiIV9;$NLl9g!8Rxi`+BPdegHE=l2Tf z(!IapxdcWf$5TT;+f!`Q^8<@*h7_2AcSr0dlFLHpsCwfU4?k=0!MJWP{hrZ*3!!7G zKG&j?;PTtZy$#n2zoeK#YsuK3NU_ZK20M*3w`uS_KItezfet3-Bwu_NaEeq;5;CIb zjIb+aQWWZkYy~UDpB9DC#Z28)Pc1+x{;3Owe0_2H?xkjZET zkx}3Ve<@1DgtF2ABA7Gd;j+&0#GeEUVBj4EJz=Pi;PL1dxR=}2U}%8Y_|TI}?gr$c zBtV++MC)beGj-&n@lq8(YdDo26C0;d~m>bBvvBf;LLOURy>8;$5#jed{$~+1&;T zL2U>UhjoDv9RgvHG+}5U0k~VGR*oL8Hl|^ZDK6Zo{ZQdcEx^&n=*hQy@a{L^KYV>3 z*KB>$5hGKPh|p+R_2Gi8wY8oQzxVPSn`gc{#Ip5haHZGo^oy%gJoN3mqfGmiu&a^< z&PwvgbVpyGlM!&y`L1DzLYcT>E6TWrjVd;0g0RdMSFKq zIy?ndv!PfxXdGoS{1i1#x3GDrPx@_^##bO$2EWGdowRNX9jM(G5kz_pbsVI)_gsf* zzh|GDd8Yo_xO%#+Ep_#)>jbb#0*|{MBJlJ_1+*lu%6fOUk70?UdQxN>#-Rx71?pPY3XfB+x4a_InoVw@w`nlcT+NnUe$3kxCte>8nnR9syX?BMQ_!QBUU z3-0btaQEQu?(XjH1P|`+9^4^72o{*jckh4CW1p8<(Q416umY!_*!PehQKH;2*H(sNjF-rE_Gklr_xE{9IuU_4uQVKw;dxuN1fa-xRdo>n5zv_s2CH;$+ZEe7U6Jq3t$e;4UfYQd zQ!U836ho25o#6J6WqNGXAKZ}7-YGU90bC`R>KxQV*s&8Z^}3s2J`S)XFrC!AEqDh1 zd;0eRyBPOmuyRQ9*8lGJa`QR&o3t~#A3rd`!$V&!mcfHJ4Q!?rQv+r4)FVP-QsL@Q z26jgud}qK?Oa!wH@)PN}T8jdw{$iPsKt$ zdE$_c24&56K=Tpsol{>)B7%&D4^{B66Y333pvc>gi}6ll(B*{bR83t#hBP5@&c`N? zU1|HQ@1xyYAs*ZSJ|M#S{xl-LCfo!=?3W@}XvmQC5L~JObF~b0Y`FLd&2tu7 z=rJKvInft=Re@^t6f{uNhK|^gAfcB%tH(bn%?6E%HxEKbWs%7n5o<=DxvQKe0$p6?jsnQG(61DkA81W=|EP=7Hkp-Pfm(tm2Yqy zZ^(r}uZk5*9iI4$9C}O#YM9mdn1~|+j=E8RMOs^U!sUK6b(#%Vo;Wo$2D-SgkP_m!+Pn78wsjr91*++{W#Mc%`z^OWj*(}PTXBs)&qz;RQOg_{? z=79PrHG$eHn$#OuO08~uP93=(*3Uqv9vH=Mi#W74@Cx$)LcseO9JE!a38gR9gn;w` zl%#@++Xx&k6Q+R|<%4Z+j2aQ$*?KDL)m0D?JU>8hGjb$|f?l)_>|@P@)TOMXry`4E zKW&~4>e6mf^Z17E*l)2?H;LWI_s|;P(9(-Cq*>>P>{)NzzN*z_%WYFa;TD*Gp|CXd z@S2mT+`9Rn#_=Bjbd>;{-sY73C=- z$RK>xnLLaVhsUp#8S;j(MB|WNz!z_1O)a9c5G_29WsfyZqD4NBgfwEYP%UKqNr8g$ zE4daQGbc66ssTtxBpwB6t)-%D*DL!u9mly-!Obc*Gq?|Dqb)^ZNIm~PDiLdH(X8v-#T;FGVF35{}R9SxcYv5THujmLoWDe_5(EHCOkW0 z<w)?Gsch_Gq z;jn&ts6WG{Ng|we%`xRm{njn7ixyMf%BU^GPMb__m-mVQgcNDh|M0l^_l| zdq4NT(8f~fyR0>7`>P6hqBKqMg{obLI+b<%>T+`F5l`^*dLtDiRZ(E{4jz?%@$`_p zgbor#T5RNKTn1L+1da^41SpyGXkUB#e5KS#B5Hq8^Q)B#13_u9=qdH5@neWD?vff7 zjC{$=R|xT!}oK011PxWKK@8hNJ96b;yG)8DbBwHIC{~;{BBuk*v|dNFlr013|ik15^xWJ#hG9nkr)7qs;U8+Tl5{s%F351ji>N1gZ3KZ2IYUBnyf-2da z(bQ8Jaq*DyPF3&&_7d31(mzL%IM)`ta9vkpC~a&k zY`O{-Ialkesy1m`DD=3&iw$y@nc3xV>M+ukw!K}`s5%;nu-e;cX*^$Bug|>PTw7#>}6#MDSHkmnNP^@Gg~{g1jf5D25H zx*cE<2*^b4PB?9azuWNP5zx-Uz*HICQ`Ns*3L{N_%XcBEm(0feVgv8L`NZ zVOR#ZjE;}r^Bg&S00S293p(yb->IlotJJD{i_qiiv}=Qb&e2_!tE zFi#-Ih*Ws!-*)#7=7g4W4tAmZU#(=(AT7EsNf=@HY8|qLu$M?FhudkUB7@^JSh?Lb zvf9CI)HET@IBazCFZ0t4Nf*Vz!er@^?W8v)NSX^}{QN8%52;b)i-9TCpsBTeZo8tS|X99>19pt!rRSX@GcZSC@e#6SL?BD%5LgV z8}*vr$AKSVPH<0*mqb$hA#pq~GuC4>$8Nf-n!)<>Sf1DAwi$yQeFjxPMTkQ{qtQ)! z7+*^}YNS3hqvAqXR8(<@1(dkFvE}vFDGB;2^%P+XMZ%-|zFraohIFy89@1dkM?Hwd zV)=uM?Hd9JZGY)x%cdNhjwn+aznoiB0{|_`K=hMpe+HVZqrqdHkfOfYS5l-XiNK;s zTu3BDsGq-?*a%gJt*Yhu>Gl5Yd@(tJ4-YO5&t0U5YmMdQ1C+E{UuYxoap<32z+Q%~ zmt02Vgf}P0g;PS3v^8DR`cfwqBj7W6Q*S>S;3pjKt0kZQ*y?y~=cD&r|F7Bn0DxYw z()Ix{^7zIPpb3(9VOniz)$LOaym8pFJVyHwADbQ4rLSkW$kir`748=&3&~~@u9Rfh zPWf4l`ma^8QSSWds!?di@sHqp@&l{!O@B7d(Iqt1k-sSQ=RP=&o*zSeCzPfwCIcX1 z(l!3Ug<6Dw!3H*kBv4NhEy2^{G*bFDh1RbQ5ya0l2Bm2w{KOE&+I9MZ+T^U`wQf{k z3vrEq`^`$OUGpH*?wznlgKLRiDpkhj6Z`9r3iZN>Vek*-+JMjef#LP3s0qr~Ym;^TbZ*s!I#46rjvIr zA;l&w8%fCa+TBp1ysPug@3s(3OMmP@IS^~6!de$OEPUcm3SV4Bi$fd)V6g%wLT@MA zsE`}~Mn97ab?R}$2RNk~=2dwPpTF^ujyC<>j|ng@cX}+J)9qQTwH*9s=PDSG^L}Tv zn}`sfJ!9>PF@c27f$I~HWZ9>_M(O~T$YMyIV_@q{B9?f`c(t(1H}?n2f68(;nk@uE zuUf9y%8P4<)*;pjlIN@Dm8dAPI8H*c(_0Op#gcQZRy*C&jS43i>e=r+Nwx?BpQC7rV+?o90yNhZ+ek@5gwH{oz)gL$Ut{>I@P^?8dugu0< z$VRMvj6V*6%gM-GwGI=4MYUDlmoSK(7Q^8A7WTt5I>#H|*ZLxI?`XGg)m(at{9m%` z{mTB8aE{9#0an$VXk!W4HPzWF2%b-sg=!gGO;|fSDL8!Fz+w?ee4gNu6*x#TUY(p; z=x?12Duy$)Iw?OJjs<(ZSy4}q#GV`Gu=;RGL)WRhCcmZ6sg>XlPs4+LxYG4}7kIHm zPt?@>_wRHx>$MGWVMArJz`i`2phbe2b6+*RG9PoST3qTFzIvHMm$wQ2fz3oMEQ_$- zV$9)AJ(2-vg(3nCG@ML843b?Py#%1k71=V#;8`F2-(QDi9B?w1#QeiE#5r0Kk}(RU2=P+v?4$^K@`7Dg^2vouo7>M`*q zK`dEjC)XDt7n62DiwyG9?Gppgk2S_)?J!*e`;!}nZ@J24&kEW3?Jbuc!86bEE#0I= z=jD40nQU7*dzV;GK2R<(wv;Sh4yj*I_imEdfgGg#E12v1s}a8?hypa6C~T zA+7 z6`@xG2c#)604RvX+L1V#U#q^+x#j%bcFc<$f#yvAVF_E*>UF@_!dYoNdH*4y%y*xT^weMnjB-YJDE_p8YM_yXP_ye1uI z3;VUr(Wei0z=x2btDAX88E((3+`rq~wJF_+dSL({T2tWA8U_W0Qa&^Nsa($@d3}U5 znL-?sg7kNyJ{$g6=p2pI`a)zw~OYYVlUtZa>B}q1)T2!~WY)aQ69LKE6No#%axUnSJ8&uZXpV z%udIgi|uaLKmaKZGL7ui;mEH>X=t-$zfp&u_!bW0qcH$qV`zA4rk}z_(NM5Zq`F44 zh$nEunds2J(Am)g0m{gb0Ha6@$zZ~t=uksZ;w~oYNt0qsrt~I@0L^?6xt(0+jxPcABUAE znj~Ye{N8gGjIvMhCD@whtV6dpy}2(k-u@#nI0EmCFgKeWO5Vqt@XMA6jD!`4Ui}}I zO8@d-H>CF?f}w-=#?n=g`#=MeDe?jT?~?!TYKb>-bGht2C)3rM^M4orr{C|Ej*N5n zVMYJ|fD^Jfor>Fa!JmUQ9Kc!ti-RB;l%^ehD=aqwEtcFWMFdeUgQjG+>My-p)MyuDbx2?SMh)j4zJ-Yt;2@XSuwU zpAiFcI`z#DMW+s8a!>jzP1oPM%sRv5QZ(G_uDlvv)zEb{|DLt?4RwwZ^49 zyzT#k;=FpiR?Zs-ef$4Q$j71yiPHy-+U_TxZW{o0wuf7rilp@F@~_A-yT~P$-;>s1 zaL4B?FVh3R&;fx~xMl#lAOu)(KqfHQm_>wC0Z~L)v{FYOq6{B2Av^>jY_2fio-XHs z%{C0p)2N<~O%CKue(Gly;KLy#EQW;5gu@IBwY1m4f_2$SwXq)XC&q&wa>Lsu?rQ3C z*_3GhmV|&~2V9 z`H{lc)e)D8iUB!dioG$%2G;>Hah!=>*Y8$Ru+ih4^y!)PzLsOCyU5a2xI#k=>#}PW zgP}$YQf7$A=jAp@ac8G#{cEXGz zFYJyea54c1<)LVEth9S2=5vR*Mp=x1#!RVNs1Oq~_fKVMP0B$z)LqMgz9J7U|8;r2 z0}?gxB?)dfn3SguwBZ~4EJ|GvWeqo4zuZ!zi<)MhEXZuzV&#?4cKD*Jx^cavyjiy) ziNklZlDk^3y~c0&j{AEm!2hb{=*KBHtcEjdoqM{D4Fei&m%N{+W9QS;zs#mE%fHs& zm-Ewl7N>RiRW!qi*#C#%9n9}HE}DCk=q7+Ly$KEij79(d-WbfKzg5e3%9$%V*x)nb zwEP-EL_Q0|$L#!?b%`RCu|U7^>NU1H#9=!}AV(C59S+2~`$KO;KQYoe&xk+t>o=FF zXqNA|Ddt(BiPb4ap#d}L#O5V)7FL9lKOC9x_FH*t=wDINFcA)gg|ES|lA!Q2!UPU^ zDZFMxU1=e(SBC)Uzc~jmF*iKDKRuC5z;5hz$g9Ridmsq3EqBNC2HeHjfU@Iyp`mEb32YGMeL)Km{>B)asr6Jr`uLR~Rc5L5kXSz_xhSTO3NC*HEyj>MFI z$RjYQRpywZCS>oeg<=i)_5}eVLt#(0Ab3Upe(WYEVy%mLq5@_FcDlrDgMbjZi!I_A z{#C3CG?PCf1GPez@&+XWB{oxP%REBl?l^nxGo@R1h*we&YHUM zmaJ=0)z9_uMiv)4|8=Zsi>7Tl33mj2PJ_QW-DrihSqz6Bp72GRbAw|%uEHG*a|e}2 zq)8{KQQMzbt^cU)RJvkuUV`VN|GUUd0ie~*bbSyQ3q$O>zY3ant8s?eUV%D_LCy9( zk466`Nugq(QIJp}9nOXQ3BOV{~TW*L9GV_ohO&ptixWXQI}bL=FlLli^|| z>~N2Ky{$TNtkiKjp(8Y3VZM?ve#dwBjII4EU*h0XP+?6$1nYO%&gxQ+s}x^^Geo9oHipSQ}5{ zr>1Pf&h<{&Cl33(Ja5BQ7_u5l1tFZ~CNg!w_m5Pa6!TyhIAjkKlOfJ?;YzPE$%y!r z?;2WvO0Lg&)-#=orkzGO>TpSM{|~$2+49HEmJIW?V`z& z7b;qaLD=}1!R(=HhBlA`Rz}A`A@LAWB?xyxhd214UJ9s1=Y#j1!%k>HpbrmYYY}K2{>0>`0kJzN86RyPUtB%3Zj~ zo7Es^wZKnaly|1B;&|4? zdrxD|PaKi8-#I4zhD(>#M>aC~Y483Ut*!?`8<}qVAPD}*LYY!Brs`IET(Z0pM7r=| zv-1j#@6}>z9i=Jf66(bqyK}x@!X8OD{?#Ji(hQ;7rnagP93No~fjmFT)jYh2!=y>R zVQXD_KTfOzmFL~RxiuKiMPsk$v;fSE4N7K^@e68_l$@z<2ha-*Hn4i_xUP$_+_5(XpM^W ze(9Hu5)uu}JwP{U9m`&FDQdO# zg7kt%Kd=o0YG7Z}RXOuee(KMACwI|~Bbn;nQqEe$ueqjau(Ob2)}qX8_Nm||vXC!)l zS#i=fb5UeFLOjD;m|yJ~0zO?yTwicr6WGtlxX9TuO(xX`FJ_cz@ls=2Xhn!86k8^@ zYybF9fX4OSNB_37MIcuH5h{(FlU)7N@kHKwK88*-c__=fBRzgm<|;lP0=J`LkOg;Z zc;=EhyUMHKA;`{Wbt0D=$)Q|WqUl98e=qUh7q<+6c92|uLl#>Pi>(F=l0RT2Md;K~ zc96rfRR8`Hzs=ZQC?nnaQy(FudQC%)x2&-Z<)6JTy~|H{u5zt~G|bfRqW(?L!&UQa%_E^{$Q24OD zquk3*9HF zh6Y_C)M>F(nX;Nnw~q;?X5{ac8gk_h?qLkDlqK!4ty?;tqg#06cJjkB6V7I_ zR%=13KQ6~l-gR~II6t_J3qH(Ga6MIc3-7^&($N8e1pzif=c8*2Um%DlFjJ*7u~qGl zG(bwZZ-QT7jtHJ(kVo1^IjJO1>KfC<>2y*pIJ4GHO8vyg=5UXvzp(n2P4P_Wc|0+@ z2mQ{U_^p#@RjZo!MCqq0#GI;S({nYGxq?S*nmPq(rRNo$>7hVe*=E^Ot<{$I)ZTt| z-xHp}H)+u%`sT1F*;}^Hz3bmvM1X;W`g;Ao8HniDSc~tJKu$(2lL;FZ1`E(N8Pj56 zMTs($ESQjRAAOrMrHUxwQ0-KJ%%KV6@OI?R@!)(C6!S{hn4I*NadvmM$BXG;MZ$ls zbuixNFObu~cP-3{ACDjA=J}co3~yo8$c{g_!dUg~*b3}Cke9>#wBl|wI{-lU8ff^?5p_iviYF^*F3ZEbT#MJb)ZB$X=Uew&MDltQNStV6?3 z6bHZz^d%2{liZ4W?-^#qYtj_gGIntGwF`Y+dsU&}-V5(ej+?cT7Fh?;AFaN|{%lD? zrZvMAh;zIZB>U{~Kr%4mjB+Tv^S3xm+AuNn)VJo>j66GT0(Q1D{H(m6iF$Ib%$h|z zZUkljd^OF_`GVE8OI&x?98x#2E^S}&M9b8LZd;c3f)g&Rt{!NAd(Mwc04rXDL0ZPd7{`MIn5nuh5#Ad2y@gA^l-w3*yM(tajm*GQfhDES6iU z>ks(5Hfoda%>c!e)nH@^d#G~oXD9fj25*}FpC$>L%AK+CW@x$X{<%D}g8LF;gX&OQ z;@^0}NBOZlz;||e0ihU|;wc`SjSn2=mrks}BLFX)(NfL!+$pE4xXl@(!QJ zDX=0u?^FaTGo+*CNdQJr(|t)S9w8Tyo^GsVGjLvAQ_-ojI61|Fs57fFsq!&p<&Qwj z=9iZy8*wqWuCwZp)f&*xd&vka_ul-nDxAWR)z#M3^X}Z@(yHpR@=FbkM>@2=*T)@h zi(c+7kG{CLxtvN$gmWcR^|g^H02cbsZ1fgeeBX5SYYIOX;h>XDmu*2rjXu$QU_q!3 zb=C;APl_BlBZrNDVUUXE(!0Iqt{t<7-nL|!@y}^zf?rG=Q z+}(qM$L-r;PEGr^+cbBA-5LprT}?gk*=_kowGG`O6cV^}!5fSSAX8u`CFRXBEeivX zLV4C73W=aM2`hgGf(oG`LeL{HlC%eZ5(1$z9 zvV=})=>&;g_#n6A>e1>_Y0WoD=a@MxEo4^SZx%=-ZdBb>M#yx;=}W;&B$69RKU4GC zXv+MEt+S|p42z0b)Cn?r>)VRi=Ob334ewO?yW_e4dfKJJs*=O6VR%>GYS3+ip`xOS zrQ=*P!GinvIKHt(>&+M=cR7PJaAB=dXZa_P==W@&t~Qi@_9*{Lj3YLu~e0?F2{Me$}W^%`Tu*7quR6$2_QU#qyK-<}#|3C%NHJwLI91OdX(Kie z1fatZJ%=MT+t7E83yM^X7^QO>2RH`ZlV-NVqcQ{;2TdW_<_fgkp$L z1RfcjKA9M@+~c9FpCniLnl%Is|Bbs`iJ?=@6b0;X294W77M7Oc^)FlOdz6(x*!A1vqD-P^c}4Ab5O`{VGQqtJG;K#b5c z82rxZ@i!`G6%2m({2%_a(l`c#AOG#W|6FdV+PH?y_%l{U7d-_Iq+k+M&|s^KNV5+A z*cC689_48la3_5f3zSa6(~2U?kq9@TYOqW>U(L41C(LI;OjeBKtgGo-$jCnCa~|-+ z$KmFu_jWyLS28v$jqVqH#nuWiP|jbjMQ7Q6eVD3kU^%|%R$LB*DEpg#cYuwKPf~br zz2sRXt*C0I|IF4$6ge-|lyb+8@QoY%ZK-=iYwl_LpO&vG@T1D%=De_2y1P67YgMjF z9a9Xt_2!1I$<|Gh;Hg%C=hhlgm}QIRw~aS9?9&r1`^Ty+IX@cK-(<8xhL1u9*@}H* zDtGe?xR_&blgjzTASc&iDXr?bvo4Bead8`l4X)6)VoP zli#qo#;};!=-y5-**LxgV@d8(DLUO48x&G{B|aWJ^q>G@BDS$_FqPC{rK#)UV^5*E*j(55m?^ZX`am;D6(qp+FdVtF2GTDD9lFf(#V=G8k`q8`ZlLqE#bT-7s4~q8SYdnurr3tVcp47=zsWKqKW1H_O)SR%(+_~ zq?u!9RGr>Mj^<$EWjN1q;pNpycQ?-xMku6m5mW{_p${$K(L04QGnlXs2*e<{1hEwO zc8e)Kx?DZV)hDLv0@BjP`1Z3@-~U-VOwju_HLCJ6@Uzoj$nN4+Zq8w{c_~kzp^;Co z>hB%A&&Kx>#*1l}hzoFkHHl>TTWV(<2tcsZsawp+X7ox4Ztl=Cn8jszM z{t<;vIk`+p)+V@&85F0fFWNz)@;uVjrOC$5nTkenzP+x*7a{3h=ACXz2RdFTd%5Tg zs&|+8aSJRl?vrM04dbdwy^}P7cNX&gHJllrp@8Z9s$U)#H)_B*ygMxoebg*mEn-7v zgliRhsg0HiC2m*j&tXo}z3eA7$|!Z)n|gZrRlnLF$4gc7i`qDNqmB8ig`zxHIBZ#y zXt+kWe&d5~HLi29y&nf&XaSq1G6*$X(>e%|mP|XUxq1W%^HvK-EK@6)LiWu_WeC2^ z7gz{iVr=3nm;Ym-jTT8UBBodHXu2i%KkC{F0A|;H$48#Hc~XhM(fHh7RVKk>x>0HW zPt&bH3aYCbai8?XRG{R1txqU?7e}3e99o?V2IokH@+Q%n-H56Qp#tk$KZ>J&++_0% zGX}83JQaYKyFVK&2EmZlT;oPIxk}7W%AE$ocuHrXfM_Y`r*QLRcDDO&pr`qF^9&o4 z-r00BU3rauywQpNVjiPPJxN7!Svn`&^+h7+mmE_UajIOVEQM5-BDbpV^iM6);%MaJ z%q>y)lfu3mE%s&^A^W~oWZYewB$g=p?5N0|qwM4WT8r=Gsq`es=fox{tE%tHFPm}K zF81xJChHsja{6n97#fgsE{04skSX!$f1Po>BlDpUJ3Qb(VE&O#HBRS7!T%lDg^Z0Z z>yk=Bs&FzoJM}YSLVygWib8;H%2vVV?W4b}x!8vqJIk(Mi%NoyJV?%1En0m>Y0scc ziH9<(I5A9WIW4$e?+DvI1Q!TTJ5-cInz-YzUaiI)z6Xrwyl?-QUX<0Xm~*&kaeQHm zqhl9>z|x4w#-2NWlq8P`QTS3BjOEvoLC@jb?+*cqPYJCseR#HGt}fNt?Q#+wk?4~1 ztFn4$ZI1J9{8w_yBGiZj+Z%TKOQ@)c^rfBs)Jxvv7D2ZD^Kdc3-4?WBV06&|?MWsV zXRM`qZ%M%W{fczIg=u+}IjM!xOB3B*M8T!T6y0gMPL}Nqx`yo(5B{wgG$(0_0VA-l zIzH$Zgf`Ky`JQ2AJ6bn%RtU2gk+S&TTvt_tiHq%Z3te3s1~J-&X&b4ncy6HT)QuLa z=nHPhVhueJ81&JTpWRY;AwVf*^(qSt-{%(SugS!wbUerT@IC0N_ZP)=2{K_ClvmZ- zJj@!fM7O>A300^bLah}jC`W5idF;Rexhj9}wb$ua*|tsVzLH(738(h&}NDlpiChT1WAN~~ik z-S-b$u8uz4V-fi7lbOyvtVSOtfP>jOrIT@>T9O^Yt){^~Y~a8=FlL3Ghe9@t?VSFm z;;)55qKdd}R)#=o9OY`8YWnt%Cc1QBPuy;b@3VJUXceJXk`&u=HHxse;+)7@tQS{524f9?gbvqeA2;R``MQ)hJ=5Cr%S4Ou zIyI8oM?6<{k!?r8I*&i~5DSqyN*=e;vsC{(u{bIub<~tO;~;9F_3MJIXbn^?Btz}a zzwvP_V(YxvZ}w-rz=;%0i}3aWp6{*&!<;HKly3(f;_>=xjAhPTsC z^9y0lzc~0Tx=YnCJdmVefd>zypT7qt5+-CC_{qLm#j9dT)g@L{7CmjrXgd*UeY11Z z_*;IfAEDx|6<;2nfC3k20KKkFXO7D{s!jn_%J)-XYP7nTz2#mtdrOdYV*Sp7EWC29 zgsD$j*7C!EzH)#<`gBNqq9@2-E4wzItoX1qKHc;mCV0qotepI zCV0gZT>6s~GJf?RUFfso{-o7TW}E+jP7&x9nj9jxNDRp~FM4UDfz@o)UrYOfjy^#t z#>AHqmTS~SJLoKa9=VZ`i_60kg(-z_s8962YV{ounrNjhCqtAO;E;pJG8C!yUZ$>4 z=z&Pc!9Y8NMuvoI4rORxMrHA1?Nla6?nk($16vJ-A7c%&(pI_aT=+BF^!#yal^f2g zTML7(^af7-v~a2xmWMJ6p!(5dT`d26rAd3u)Rk(%VacAqm03U6 z+;X_xNS<8U3%_!%xSvwNj}`ZC;BSV{Osl^!Yb3e9RnU&=VXo5CjCC%; zmJzqk=i@JD2?%|~NOB?Bqk7xb;@5SP9lBBUnWea4O+&g}IkwC8N$-1aMIgROWtk6w zZTVUz53-;~CPl;^m?r&=-gFWjRfhw}2&|Mhf{dq*SJ>gd5qaG7L(WG`6QeaenVqsH z_$z_Q;E!s3q>jO1i-KFshKk%2HL0+>AuX z_Q?_RNUONH-z)UI5yP3ze5JSuA|b?wo>U5`w8}S@lw>j#)rEy|5o98f>5)AYsuZzW zn`(P5Hwoi^Ub~pndR8ZA*M3oEH|QmSr*DoV=1sHW>q)n9zBXultp)(#Ln!4_ARtwx zZlrBWVQERlS;(QVQCL((0fCj;!rr^ptzn_^0vN;Q?xl*sdCd`TxSQd0py;{u(_>89 z$4RdlaqeDREZ8CsqTYO`6+VR|RBUe)8XD(Jzh1U6+BWD7<0f9Ew)*-#%J{*V>=Q@y zZN9U4`uWUK`nMF_kQo{5&D)JqjJO)P%3?mzqlKJarMtds3ly3RSfz+PM5x z?4b#~QPKz@Vr~lH%@r7~8z_UoVn9#=5QaP?98h#$t2kn#U!^`T3((bRoYpmL))lu< zJzeZ?`#mC6=RSMIUA+R{(&eP?MkbMJo^2dvVm#xZsVkk5UPIECWlK_t&MHn@qemsP zcNR>uYY)+$-{u&nn=X(^Ej!VgjzgQ|@(=<)FunLSP`;OIP%N{E0>0e7@8=fp=Lo$8(g~?B{0;9p;Pj$ zj>Wxbrm3dq0rl9fql+l3r`(-eS4*kNyX*CAO#k=`YV=92z!CqB>Gqh0HTSZ1Xe6zzmbFWi;@huyFPN6BzNASn}f!0Vx4~*75o+6J2gjTNB9XoL%+=<$KdL z%_^4sm2tetp!8FTRd~qH&1~3GPm3TgglsXIoT8%Z6vjLH5q|I~1ix&T``|_uX3<4l z;|lY&cw2CDVM6PRVwOa!70@jR2hxPaqqY zJsKV*iFocc$NsQCyNBtgJ7>ik!#MK%)??YR4>a57u|bsSTcj2T5^=oCa+k_GXQY1W}2JsgRcT-|X9L1^_Lqu6b!elf1eXN}T>FowlHVFE&S`ZsrBqj7#O>A_@cBBhgO19?6GB$rzeVbLMa3LykD@1>76 z+xVnWRSzcX$2)0aWsVWe!8cwnHOL9%HJP-gQ01f zVFFn) za%-y1SmTpGzvR&H@ku`)AjDpduGAlb!B5RUDGz;6AIzz@0jY+dB+gE(8_;x;+4-xa z<26!x&rXs5iBd95BB8)|Sri#WxY_&okioCb*Qe9n*rOQFy_eKAlB{$?#DhTwAa?Y| zR-XNy;4AE-Hyd4}Z{J}cIq+)2BSVE7>U9+S3hFj-isQ)(?daNjI`Vhxk2&Im0|kvM zZ+j?JPo*$rveC@1Pb*05D#QVW^Avcle2zZ(J%whUi|j^XE0@+p)vmSpT`WT*D%T}y z3Z?Ev+pRXV52)Kye;STaEM?%Mq$?0&WA`d{caXT|9%l^~3Ued_j!PPXAS37=(dC5cl`7nFSB zJT!}k7>n3B+q10)?y;G`gUe|NX6wH7@iiz{^iiqs=O@cH+0RshP99)W9)W$3x3Sl2 z=R*m-t8ie8qwaDMOJV2s7#t&Q@|jrTTh<5ezxX6S#83p5n|G>!uJhBGo5bv?SAA+ z7az}VviXSzO!J!_LgA+&>VU1HKI?M2X1bV6@>nSXiG1_OrQlAaa z_V|JN<#J=e|2lGOZAU+*c=xoe z4U?dZng67Q*6yuuVT2MON~Pt>q|ZLoh%oH_-+Rw~Ahe6simf$KW>6izpI}G;mi2dq zdb5cG*yan)(75*_&9flLzlaS)g;|6d%Z}e?g)EFUU8)&p?j~N$M;3nvCz^{=Z4IXhrv9(feu_u~x$xhQfMz649B3qe1 zA1J0V85VlrmH^>5%m4uBaQ1(jD;rRCrXPQXX2rTXH$diK(GGP98INw27$&F0oo}x32^o z6iJuAkfe)C;)qqtr|uk>-I|xU1HnMxB-PLUzS=c3jCP2%K!kusmD4l zOk_suR?b3e{a|RFEebCgUg)5~11(U8b(NI)U#m6uYpSoa1^Ek9EI%ks1Xc=yei20h zFIw#SmP!S|F}vUe^O1?&nDJKJ=xbz{t85oH#WR^E?q7PXR3RA@tfcXKVAtI$ynl5a zy}3xP0C=N<_9yYI2q&9WAoei;IYg9{Mu3pd=;u#Taw{5mYiVfoObiXLK1C7&IO8Qu zgm1*>1p3alSbt_KO7>8xavh1Xze!^vBSvtwvSV{7AWu5v0LO-j2&p%vf%!Si8VEpL zhD-(Y=0AqGogQ{?>lP|#=Tn^$92B@UW(&0py`agoZhr zEZN#|EAh=ia}G?hr_X0uKjMS5e-q#Qk3Jt9*X~n=XQ(CG>80YmNkdvnEK3>ELnw(c z57ND+Gy!cG;4t7i9nJ3t8=rn88(>^`jbmH+{@>gK2o;uD`CJmob{XS~7Nq_(d?-?> z6R_R?mvinGj^el2(6@&#EY`ZzJZQEvJ+qUh-ZqnFB z!^TPDG;DZozI*TcbN|}U?6qfRubFoZ06AFu#0|D&JdtJ_S4mTQ$xQ`PG9GGww}>hLXV>nn$kVgmUDj+gWEqNAu1XhGi~GTE0Dg8)4AKSFp8NG;W0e``dJ=^LLv%GOaihyP!9LRg~N@_95#jsp#0z7VC58iaz% zWtP_&G3#e{Li~?*O2I%M?(}#~3I!8Ld|sw4`6=AEUQ(=jW7E=7Eb7y-CgW^B3~f+hnX>E*6vc}UL)qfxBzr? zGK|8GHqnq$IEF0OXlhABXc$}^j-qf^NFFIx06S#57R56?m6}J+=C=#kkYf+uvq zm{^E@Zr2KxY|0puX9dB1P5k?q+#Zni_wtfb79zVGGhXTDx-fperVP1vUR6tf%Hnvd2I8b!JrS8?JZzFHVBROXcUW$Yi4L4)ok!gS+uJlG4O-XA z2ymDzsh7)GMK4tQkIjEC-JvfcGYp__jfrQ`E`{U|U9e9S&yH1A=tg8MYp6n=6NL?LGsH#gR`UHnbfGegNvw&tIhZv@`(-L+2Lw=7z*pT0-MnXv^e7~;4L7yiMJzb9I48NI#IAI{>t z>JkromATa|JE3K^l}&k17_R%=T}wZ?Ri6Y^D#hfux7E`e9N#4hF%#CQlNoUYFTYxn)I`+P_zOYhVl5Oa zG~jzy53+qCA@2R@##1FGSW;PZ1w|B&*15v4GDHD4HTjUyR+{R7)clfpUW_^2l*x@- zZPGzK_Y60siVwT8$P~^*fQ#00;mXcHzfL z>okyaML;4oE{zjfTb$ZlMB$tR2q`y6?%_TY4zU5LGfdur*QD)ZB*cAv^=h83R-^sn zxrUk&PpQW`r5Q!{gfi!G%J=b4s%TVh4Qabz3&Et}{qCa(iT5$eEP?8Gqil?iVNEId zl7eAZ;531Ew)<-VZOkWS5tAP7B_kajKu}Fk++wv`!1{?Lh0A(OXq_F>R1{;?71gpZ z*CPgXP3&ge6N=U4gcYrxz$Ovlz-F+~3Irn7#idF|G_K!)Az*Srr6+>jQTIKjJuVzvi_G z;DI~9AsqB@6EC};y9go%w?zYakysJe4j~S>gOF8;)hH1{q(GwtFt~O2urNqCU0*Y! z(Q$xyNcY#a@ClOTx7t1!l50Tsao3C~O3wNjNK-EbjX-UkenS_-aWe+cU`|jwxz#rV zr)iT81moa1zKljH^`uu-X(!bAOXb_C4LR_5FuU zw9A3^L$BA^9Wv4A<={ozvO$pj`z*y66>{F zx0i9oNi;C**~7$eL>RR1ldr<hKmy?u18$)&Mixw)R@NX9Cf{h zScb+(9<|d#1nojoy`4T>-W1d|DmNM*Q`r^QLPt-w6V#KYw?C_RD3?}h5??L-1^?My zXLeywCU+G`ID~$0z!d%AUx@Z5+OV1Xxiy~awy2arm7MTD9d(7jjP$BahO1zpaCcJ9 zHwB2@5Jv~s@Li)5%6`(egXk=qZOpN=>l3NgJw5*U^`K~nSv>#lD(uMQ>pA)AJ@=iC zS#R^iU?(k57REDTD1s47(v5fdP_5~6 zLK?5Z+;mpGDSd1{Q(a`>@=j|E&wJla;PmI~`@bLa!`3X+e4~kF15Rumgd&7hn@Z&x zfG(ex-d6BF>-{^T6Bh@A2a5Xwvj_kR3H-vHenm+i#=_#OF>*SUmFwD|EKZJD&hjk| z8B3&>4~33oi;vFaG-3D_-Lx9NFiJ*+`RH@Rz5FP}(^2pHtcQ9SdLnVF1oFI4ceV#+87@6#ygzIaOj`eKUsm6ss6(%ybA5s~4j- zKQSV7&;V!4DTR;P&c>+5uFB?8p<#(sP zvH@rb{Y3?4oC*I408oP+Oo0U_H=gKAxE!b7D52&su^5@E(w{|_5!Nr#i7QxP$gP;* zL4Ru^hME9JYCr2#ah1O1{qrPK@l28Km3N94rGUZGrwv!^byo)cn4^@x4VIskWon~9 zTXD=*lPx0;0{n%7jjli1G5#2cw*BS$)3k^SvRaYz6+mF(LH3E+pbVjn(Vwh8#i!BL zvO)nGW^kdJCfZ~@F^CW_`0#?pH)@b6xW(dzYch@3l-YZX1>@7=(PP>$8Qf<*yd&Jy zj&UiUt&X*Z_GjYg8rTttktP{M%5NYWN?#w&n4Id&g+xhuM_XCqSZ4aeNhy zfoN}EW~+|0+{gDRrSYBq>!W9Xp5qFUIL#tsTd}5y741)TUhwrpTsfV1iJOs|cfLg} zUmdmno}o63LpZj0?g96x_A)KS&`RuL3h?ni#GM8~tI90BMi7sOMAJJ64&`Ezlq+1Z zI+?t)+j8E=_rjOzIwI0Ax;BoM=!Vg??ZcpH%Ph`K)3LolTQq#9%f)aY39>EZ$jGrG zjGPUFA`d+`5>Bl6?l-p{4$=>QQV9lTV?C&eApW6)o zp%yD|Fs6w19x$pYv3e3vL;}`>wu%gVbo^=jn4;QbddCcDSEL+d{2US0_D7FX`C$aA zS3;yx;tEnBU1=UqR9v2j-Y>~bj8EUbkDo)6(y+90!xRZuqQga_op-@r&RyM38I4X+{T>LTSa3nrao57 zFB~lgPUT=@s3`C$285x(pk^6qJT=hgZ+rx)G1B=0g>xJbXpG959-~i76emfc*QvX!n5-Hepg@iMMsgRN2SKXUq*w%slnzY(so8i z+#-RwQVQEGv>{5H%&*0G@?n9ND`mO;q;vMU8fna9a4)!LjlG9>;1;;2U8*vwIR))q zbz2g$9BRq}lw6Po;V`8$m=J9*Nu#gm)e6tA+@Mdu%(KgnGC{n zH%w|8Tbo}tIg~ssHUa=JpA$5qQDL(AQHCiq4Yq6OH$HAox}`E*2nyPoC2z$NW_NIG z(3Z6q(0*-LZ8cHZ>lHMMwOd6q@jwkP!%At4>~yK&ppe)}u-;E>{S0o(os6Y(LXSrM zG9&v{eWiwy-x0lx%rmKFy0q;`yvoP3-sl?#=8v^#a$8sx6P4rlmj^X;fvCCT!@0Kz7d3`$$GvB!j|Kz zOp7!>T!D$BK}^8iCd|ag%TK6C;_W|)RXaKdZ7SW~z_eS%x;b{5;zX^^wm%^p@!U83 ze5Egtk`u`=MA<5a*`g{1vJP}Vk)@OezrOwFdsgAYmX%dsvqk>J#hn=lTAZr3rT^!9 zOkp`}-tJKC!QlNjx}(sq?hCPhCgxuVlqq@x1LDbD*+sD`u|gQGE|V?dQ4m2D*~weS z7Dl5>vdQT1w&oBJsbfPa)gk=a%#WTDPCC~^wPM}#v9j1BT@U1(@vYEk&FLf%xj4{& zV_tRGkK{wc1ZU0BggY2Aj~?@00|M^usB0J_vFhum8<(ahxRy0O z%SCF>jN(D^sJ;!NC&eJ$FU83V_^o=bPD7S7t z;SU+{u(}B0&5yL`xK{s#qtA%Kd+ES^)3ujCGK}YGM3;tkCSQ^mgw+ozj?eh^frBI3 z%j4KDEqiS@$X6F|33*$f=DXr=3T0WrCaP3+c(#5X;l`R>@TB&KZN56$f=MIb`RM5T zQ2ZJA&ebo9cb*RSD}vqfob+d@7qd0+VVm~uo zvq^o%BH>UZH9Y@=W(Q9BG@z_nQE9o%t4U{ z8y6&o)BZRtPsysQF?JQ}X0*)LB3O@6?DQYvwu8d9tX1voiT?RAb{h#%Dqv(6e)3A+ z=waDUKM|Mb-*l&TNtyg-y^!ClpwxPo?KCT71xQmK4|P)oOen~q5##zMFla_6!CW8C z24AN&YGRFiB}Ry%t&yqk?#H02XWT6TUi0Au5c2}3D9PW&oUGf*)r=cE~@7pt;)5ry%*t{e+)NEE7R zW1s4pmSOEYwou-?ql{bP)Bz8pGeJV59<@qQB_^F%w4n+XbyN{;w%@cw2mi&bvRlQJ zL+0f7oxax1D;l0e=NW~&j+p6Rww`J|M_=1czbnxq2CV9}#pTpbNWSbAsi;)tG*kwah9@x#4QuvZ%St5ddL#=YYAHvI|)MesvGQrM9xx7C$fus2B89-K&=IzDKyR~yo>(!a5MHmz!_FJSXWEtI#M3~d z!A%PLbwhOtPvz3xWg#k`9VT*GDDds z-GsGVo}5|XIq;Q)a!46=e75^>0QKWe8=s+DB#u^31 zJ(r6Sdpwl+#u(C6G)R*KAh(?-rU>lem2be|5>tYjk>6Q_guF;EGs~LWN5E~Jy#tk# z*n}jv1jWg2&n%e|o*$K@&UQI!)yW?Egu?$B5dq~M_IAir$99cd>TdB9Hi{NAz3o|A zX#CTuW@P+vIYAO&L33=?MWQ?iGlYkKx|b6K03b+Ifa}Dm&FZXh{;Unt{5I-T>y?S4 zB)m=Wn)zvOFTeYs?^i_Sd-|C54tbEHzt^UU?2NoX$ZD|9k*59k3D-b9mQMM%e%VajSoUisYxgqDB= z_^q}G3-)l~ZtV)3TPDmaG$5?|^KBFiHxG^y-HrNsK zMX_a&W|!XHmRbK`f+vC==w9*0gN%j=m9t`o{d&W{qe(~Q$|XKn%qO86qd%#g(>ttLSPkJtpwjyBp`qcQIpz0HT-0t2_t-sSZ_3ZC(Ta*SFG>?wk z$5*ZeX9GV2gYB&djut847J(?kU5;W%7YAV;nB4P(WYho7ZYlpPWxm!Hy*R!aGQson zJ%!o-dkho7H5{r;Lz!4@rJugXWuJ}n zKVFH%b+M$7{hx{vL**9STGW`;rLwt``s#ffeKrujA_qa=$$n<3G~>n3P%Kr9$6ti} zQ;5Tm2HRXK8dCYsC?M#FDc;Bqi5R2k+^*lUcsh*80V5`rh(j-Zm&@&~PMn2fSyZZp z@4bmYrEL#74_wZa&HXGgaQS%~@pF}0!yzpI$3vJN^_D5LCPCsqCAlqEC*frvFj%|n z_HxO_|3E3CL;A3o_a=-pZSef9`**IE7kxBMq206{?yn`fOAl`WGoq4~j1j+nX1G%9{c06Ba+TL^Cqsr0Ng+6xufO6 zlm63;5C`3a=;!ANXO{*p0b%&;@xm|e+!=)@e{j=j*+?r?J~6Iqk72cpqCf0@U92Mg zOOH(##bol29IJWpjISMTegf%h1gzV^=r8^%v&dq0L0#rh4uQUF`T2k2n#$7d1cP~liJ?RqbTdf9GT9TgJqs!Ce*14P(J3AkqNnoFaf#a;kw>)4L<2HAW_ zS`dj#79<8Ha3K_h))HP?+0WM_JuvYy*Cp-; zehZDf=-fR{prI$BR-46C&o5IV3+{ErRQUqXN~s`|R~=|Qoh-3oScoPq(gK0mg-e@u zxHERygF@Oql|UChn)B#U;6tJq+#5ctaa{l(``i6egl;(om`okGvAW0{IyBLAFUqa? zjU0Q`0LBPJEoZqwf*-bwx=BL3N%CK`I}Zv=`Fz{i6In-GyeJ7##$w@=D_u6a6#oF8 z2@b~#9k^K29wJR8tHisDquKzl)K%d%b0e$DDoXAd#PdxS(T!(vQrFg^+)SGom}GM; zbqu|urgt|t+Q1(#^UxhU6Yr0^#&uLn-Wi`KrpoL4(ZwpSKzZl&y3ZXc-#Pu0c8wQ% z(y_epulkj$#j#RmG&n=b|K^LNwY1J+#VK?l*2q}2_7+uebfHXkEN|;LyL%Mg;i_Z*R0#zZI%mhHbI#OP;zb_)&g{XR=j)>u3lLm|x%$OE6hu)v)dz6k*F??1PPK>F=faw0t{wH7r~mVjgJX8t0> z$2=mH17Z9w&8itYq;;SzR*XJ+B+l)aZy+nJ6CT|j2fdN?XEl1^xtQw~ZdrxB6^V~% z4Rypv1#$3r)D+u!zlfxjDgIOHio=|NE+&v}R+RsF8Qdj@&{j+i|31!_A5&?*%MF5#Od#m&Iiw;Br#Q~z$VQ62&D&7gQ=#@LiLMo%rHIr==rB`rC`CpNW zTag(|e8=1c&l#0gJh3?ugC#6%-~P2PFkg>Rt(Vx6M{&E|j-OG-Q-Fq#(<5Q;RS-Uh zjJ1%WJSwTOym;fRI3+vuiJB`V?EdF_X5qsgWfos$IQjh*2m=L&j4-Q96&hHbY%ZEl z{q9f&*Yc8EN5E-Z7>{$-GtTH~7HPgxU}aQFBXba3g!HOrgLnGj`RxQ}_vm?TgBl=; z(`6a-f*Fim?hUh_HRK-RS>&^7AJ*bWZ0AO*QsK4y=yDiXRzhh;-2yY9a9Z5Vmm&V0 zsmrHNN37vA$QN-|X6!<6TT!`7cX(F?S&Hv{ua3l>OQ8F*P=zROs zCz5|-iQaNOe#3D4qpQ8b(BV+mY6F<7#lD$c@jN{pOZgx}cz*Rp*rNS~&yBNDn`{>T zRJiHmeM(r3+3LgP8&^4Qd`8p0i(+Ss_7ap%GBamZl=lCRCAY zBH|H6jeH6=I-rXA4g#qL{4_pgO_8m?#XN*Ms5|6 zN8xJ%LKM4vhzl~n*pt>3G^68B@c0jLzk|YZEtc#oiO)hAV*=SIjWEmml`hL%Y>qkU zdY4e%*zIjOn;C!zHL3G7%P|}S(zq#U?i6gCFr=zM>;=Pn9QMbR+7TG|Jn2 z{m$f`9>9+7W|P>FoMbt(%t2wk0>jv87KEMx8pT(W6^V#e42z)dSRY0svutf7_!)#= z1e{0$x%$FD37meFPC66pmc1yoza>B22IkexEPO8pn#c=p9PNQ zAjDsZCl-Ox6x;2Li93dN2IrhHk|gW1R;j{Ke_xcCG8QbC&5rN#Eh<$(M~ushY--)U zZYp=-5xRtm=<`Iqb{?Wqgvft~QhQB{y)}OLrjpY{;8eW`$>pyk#p5q{s&EO(65D5B zica!@XZ#^u6trstpB{yjVs}S0g$O}_aP*8)@PRnwY}Ts2ExI=EZSG14JM>t7eA?|D zUBkEVy9i?Kza8KX=`X|gZqMtyfTY*64Kg?rV(dz}rQ558jjdm7Q&@qgD$8VL?{U~y%C@>dRLh@3Wl zpNeEiJXQsK4+W7EX(@0DhGNJUhh>#Rn8T8%^y$N-oV6{i70wa{=#NwV4%`oYfywf{ zckZHl+|ONNvh(TsYSC4pq#r*P?OgGuW*tTa(g6?12Qk8aG453adR>Tv0}1 zQ&4mg2$cc?Bc#q6MjAbA62Di|qewees7Zi7HI|p`UKI|#L{2_+0x``#zIvSa2wkF8 zImv|yqFlE+K~vdUR5?o{l%2MphqOS|!mVnd!d+&Qkl-Vd+q=^4F10zsRfzAp#v+gQ zwhruQujd29&i%p>+E-zG1We!^g?Gy@Ap`Kf}G$w0(d;#`&3B&TuIPo$kN`C}B?8DQn zc)M|SC}nqxt+KmZkMjC5-f#}J?EA0+3k#8NJk>d{#Nhou0Is@UlOWM~xw3Xnb)lL4 z-YkSoVRLZ*$neEdB%3(T4-%EcaMlhd8rP@rXuSS_jfxZ>1Ue2g#*w5v#5GuyDO9X< z#-z!{vDpn#tk2nh?_g|mc+fUoP5pY`d=tS)Z*IHrc%YS&i}fYjVYKqturLZd zcUO4pJ(o5$DST;uO-FKDgT@~EzxqAX0O(8`wa-ku)4%S_iFGI*)1_4D66mtG!&D>q z*)#ABm9zBkIvd$P9Mdy^GK7dvVqbDeVQbo%1w~5d4lQ1pZ|5FkB-BqUZQaG|1nNV3 zf@afTGMfZKhE)1ZhE95BZVpxGOGh>ZgT?x; zFS8gs;JWzjwu;Rw_%t;|swy=sR`I$xu+etM!Ec+-UX@Ur#E<$Sc^8h{s(W0{f$1Aj za!dSX*WmbsFm!-I%54M;0ct8s9sM188?vsKqblpIgmqZ(&@j97nSRh)F&?YQsX~uJ zmR*f!3~DJ=swMiJ>f!JKhhy&oEZeu&g2OxMF~sW5RKK}zGLFeOKGm%fqhOlbj+1A+ zvPeZE1_VWJggVa%huoJc*(8E~Yevt~_?_-wC!a7v$tb-n7000op@w9(lSlG1}M_VTjxk33OX264vGu2Muiy~#z|=VZ7p5w z`c(G=zjS)BqQcy0z1)eNi^V^c@w-8KMfQ3M-@^FQ-D51DoPBL7xdHxdgRR}-aUhRC z{}Sm!>Ssbp&etjhwoU>huZI;=w?m*n{v-pgveJgj{|w6PHHHov{E1>%MNt=g!HQ5v z1R;7niPT2H6}`ViU&^bA2z2q3P9@V_ag<-C60ky7kjs#?FZ)qYH=2>-k~h>EBF#lt z)$o@%(Uf1@Q3}X8*My2MxYJ4Vl^Cl-i;SrJ%g3Re9ysc2Y(V<0xQDzrF% zBG1JU2pS-5Xj&%gez0I}e2RheO2%Rf^yWM?EHLZt2g_q}@;qDU!!#j`D6(JUDqwra`kko(g~eM7@ovml$k! z(s0_da;0r|*Nrb-i(pOx)zCzWVn}mEyEUD4q#W#nuBPgqJKc?K$CMvgD}0KJze7zi zEumm{bZvCPbKzOuKiixsG;;dE8KU9AGq=v{VNue961mDnOAu6;IA}5}B}XHeE-|24 zCe1zE0FbK-JFECSC_apd(|F2`aX?}-fon-&zRZC;79PvcfzcHCUtU=Ic{Q#r7C*T& zlq-It?w#MC^SuIEdh4h$#=#$9zMHzCAlYx+g*$w29FyevU22Iy&M2J?+-=uwCiyHl zRm+P87voQwRYj|#s}+?f7Favvoj`K@(O@VY(k4QN?T?9JDhee&vt8!zv=)YyCyT&` zcX*a2lSJhq-mt7ZAP=Br(4=kDE~Nc!a3k*^PP^iNr3d|MIIV0CcI1x~~nfb}-zF6B}i;I(fFjLt799Zc$A@ z)(v#G_jjKD)=ftbnWm1#q3q+oS)L8&^+Nt1!mp3Ni*~NB3O?5QSaeS`>pys6%$&I% z67xm6 zQ%|vwuA1_MpmDP8!NVb_v>7(Qh%xc*RNByD+N=xb&meerpfziCD)v2C=HAm7`NqW1r{PPHTXE7_x=0v_e0MG+lOSTL>W~*-`uJhd#l1Iq-7hU(nxLu86ajlDA6ZTH7*)vcmWo(UO=S55VGn5FDUXqYYIZ2Se|-F>Km7zi=!;qm@HoVvcOhh%H3rZjQc68f2fE>qJ|5r^uCI z!XG~*wxt92)^I&Na|M5&8tNuU(y(TjkOzHJGBkbVjL1>D_x@*7jYNj*fm3I3vtxb9KV_l3__#+E#i)bh|rD zwVZd`uo&vVsI>6nZ)&UlPvF!~6yMCa)(A9xgUiJ8a}$F>;FB z4w)q?5O`SJ$*VQd#Wrz`J&m$bPmz2n9YRD@fw>+~ zrV{71$T+oe;2ftUFZJbAy}bZ(kcGay-jvPfy+=R0N3A6#Dm>nH^udSweQ6koTf$aY z*^<7YtK*+-zTAD^U=(0Fi}$*2uwJ1h#t=wFFwH3b&+-mgL~pUr=>HA*RQAnQClh8c z4k-KVy%{U>|I5$v7kyCCKT#)>&KP6Bk&>V%)jrZ`K<*}bxn_3202+dvg@%8x#Yf#5DCC8PLpdT~zc>7TeF@4nF6*~!8i69g4 zKkYCN0AhFKx}%$PF!9Z6;Is*(+JKz*dqoh1WaqVTRuGItdLVe~d_5lqhSQ=>%)TqV zKZZ6wW!#@#?W+zOA6?Yegezx+?34#dP`+*Js&D=1$+nhT*O5cFOC3La9Vgf442luU z_Q3zM^@kHY`GWt|ZZ{cTa7xGO|F44crVtU6`F^X&LV!pPLVzSPSyyTksb!0l%2GAU z5lDxcDZVkQN91HCr4d{V60=kSCU8QzoaZb95LnbaWLcp_kw&p9y#|St;9vex?LJ;8 z6D<4@Jd!R1J~-r9Ijz!-7KSfh<9?jAUUA6c53MV~G-!U^DjJn6j_xkR5Rh+XBlD+3 zZiX`r6rpY>(5_w5v%8fOrp)W0jaw;9H|$rENjoe>S+E56&#yjzOMbR2#>zOoRCWDO zjqWO}{?f1HNn<$7p_}0N@Zz{xd*}V`c+{Z0o~rP;<=H{+ru&Q0R;ymj{`n-rQPRw) z^X;(V2)Yt+fx$gql(E5>OT~l0@BfhqzP{OhU+eEZ`_$O_zmIOq^}d_OMN7`oB2Dh_ zx9JdYax_(@$`%fSqL#p7AX*H!t<8rx%{|FPrHF|N0hKVjQg#RfJIsiON_3H^v?N)) zB50aFCAzMA=-(4%+V9(vYRQ}ZR&L@b z`SvZ}%nfYsGrKdusu^X5+j8dEku4eyTB1L=RXPn>d@Msyl0qbv!i$1vl|RnxIa9+? z*=k0Orzh94n%N=? z!6kWd$t2sY3^nyz#~6>Ig^uHNr!NFdAT^$1!-^&I$=H+2B}tglNupNw|J(obqvDum zj*yJ)Qp;~jt6YAn%hCy~_3NV`-Qb+-bXlrs;B!2C8Tj?1q8K=i4iNFks)=ydE32xerk}xa&fT)jWOjd(o)jg^M_Z3|ArgE`!hs(k)@HIVC^|~7s zbbwB-9IuyOmXX;Dop5ZVm+))&e5V5w75;5yuqLY?DsJ4(@p*~$TV1K0a5C-m6)j~( zXI66TS=`VzSc1Lp8haY7jNpkSq!yFIQ0WC!`0@6Q2gVBB#)?J7<)+G_mlt)mAAH$= z=`my=z@tm@7vlp`9$LH817!Vy`E9R>z;gLlUm1yNb|?2CLiOLUnJH2DB(-@Okn?Z0 zmZQE-4F&37)vSB*G03N51((D;Pcr@8k52T2osBIkonua+Z}iBjX%;Kh`C6>m2x%x}2 z?&Z#VYAjAK6HXDLrKO79!JXuC%AjPa6IX~==glAuXHruQ8H)+o_bszBK>S9w2 z3^9jPB<D>Y55um1H|m{ryuQFdLG zUv#VG^fd%N{?)Cy)(+eU+gY6xK$m^tgRhBsBJcljC8<%3uJTZo!Hy0pSjZAhYcGXY zi%O3V4#Va8qN&$UEq0U+DMm>o6sT+0i)mMejQ@L&9*OS{iMfZ8=a=+ck zdz0_j6lLx^`({Iz)uNF1*vT=;A^66(cDKjV>bhQdC6H;|zuj8@{}w`F#L3(R<+Un_{O1e>PswW^>u&DiIN#k5wRb5zchDEZ6lJ<9_t0 z9f)?C@_wGKWZwpNi&Caj0C;#jHhHbgiG~d&5mAco0`ZU2)d;^KNk0w`%~-25cQtKc zG9+AGUQa{2cP*@B_!o-Rhn0+$j&i5^V6yq@;qTH+-Dr!qCD+@#c){i(jr!}t!Frq8 ziZ(f$o$#49*t2+5Gx<0$4_(qYF^tp^G1`I9~^Prv_iHc2;{BaCTduu{LJyC%tZ^E|fh=>2$e@&S7OMFR%%(nVeR(hI=6YEU#X<}2` z<95fRmMQfpa=F=R9h_g!QfT3be_D$Ax7CY%1)J z`g(;+*)~1?KDwEuH-oh~3GYwa?-%DkUcQ-zdRlgoHc2LZoO3B(dXsGZV3K;PKT|v; zmn}b=9h@ARs>{jLL6|)NXTt-xEl7K zGUc_IQR%EXiS+eJwOc4#uqpCOkb5dl{Z2VzE=@lwNRVkIn{2r#0cllN6qZhX{U74C0nqE# zIQih7i_5e1e|qm_c8w3NMj{Eh>wTLndh}kJQ5NFgL{8v*$bIb zF_*kw9?3!V4UJXZ;ucrz{fM?iUqfxp5xx@|F~gJ`_n;P5PnJd%Y_e_L#T#7GxP;9! z^G9AA34q!gWEa zu+YhW6Mj;Uv^HlWB3{xKSIen@m0p7a@c6;+S(2+*Vfs_AliSvnGTgk2{A!NY9BMjl zd-#@XSS`#KN!ufYh?h%kzO4frJ3*~LrlsXsT^}v8B>Jw}7SCgb6`mWN3(P7<(!ev$N3+S;>Bk5arD?a|P#ts2 z>2*@q*~xG#KkKT?C@xCG;X{a-m{d;Vsc)PL)+_Nm+3 z0X~0}KkhW%9@fZO&a=vsVs#_QLQ6M$qJRTcR=<#J|9ZcVFAuQyS{0cL9*9+ALrm)x zg=w#36i3eHS87k0`zNx;Cwov7$I~z@EI2U?t6sj(H$lIMRPK6}UOq{MTnCX*A#aIV zQL?<97d1)bnqxdj**WS&!)@Y6X~jNYKtM(B?QIa7K7MB;cbGdHLTCXlcRIzW?U_Es z(b`U2#^;4|z-+&d%H=6$@9(g|{OmZcCYSSA!xESLoA{$~6|6fW6KA>Ep75ALKT#WDqiCPA?7{sgcgY*7oq${x3_ zsINR1W%m7zjGh~GsX0i{*4E&5r;pGC@kZh+N2^w0GvP~?5*sh`M8w<&GdK{~EqUft zB{?&|hLIn?>vTdN8Oth}xKTTdzr_~JiqbtFdnX@OOg7HS!GrhI_3tRh7vN?7)e$ld z2i4nOsyP2vT$t`RJ#~h%P;U?aI~on40i3}SRDi0ZvX&(;#^k{@-?TL$tVVWm0Et=} zT4++N9Y-?iRBTRX^7G9Xr{s1`sGYB8CnRnp7*#O``gw0(txMS1lV=lg>i@v`^Re0r z&*yC3{^a+u$t~ot1ipTE&@dE!(0#^J+}a2L$MCc9o>sQ6Al|h3zRI7~?X_=MKxDgx z!G}kiAhw}01w7Uy!LHe4XTDr4QnGTb7nC86t=9hk|V;i;nQlkstH&Z(9^WJ;hciPLiCLV6aBdP=~s?q_Acvk(_)ClG( z-lSbJ?xVeEQ)I@!q-V@jPyJ~7ar9vtxz$VGR~&F7^cf5UXNG$WI$kb^XA>DEI8oaj zyYzCZzJtGiG>oMt|CAcb``nu3;C(Xc(9^!IA!pus)i=HNQ+BMXwO}ERG*lj5-HEe? zShAjpOg3Z^y(jVEO~*@fO$5AwdN02uaob5-!$)wg=NrWVg1{<{-@&NSYtj|wEA zi_f4JiDDFtnE(PPT%OQ55F`?NvG_Af$w?!+qP{R6Wg|Jn-kmS#7-uj@Z9|O-hS)- zxW4MWR9%&PX`mX(@#J}b8DyNEdvR>DyH#3~8Bs9!I3qZCNo3>!vj#kk`5axJ=s~nC zypwNL-&w1TL5+fIOO#Rj!2}{%?p@?m=8QYxftAxmNX)rKU5zIF>x~Y%uB6U_(|i5T z-!%)3BINL{G)nb24hl zb39$1dybE6O(nKa>#7&wfT+dB_Zg$tR-$V5=~reeonnZ(oK~im$io#|5H-{BP>6%w z?r^w-`%#Ekh2i~}lXo0Rqo{@WZkZV#As!7zN}c7aNL<#Whben=;(XAUD~sQKPw%+W zpEk~4VpvX2CElued%(%;D`(nK?nr5jHvu&n`hGWb%?MWy9gCwRyaCjpUp>n`Ax6n? zE0vi10%kQ=7>JtESc??GcBEJ7Ig^2x0+PIHmu+20k~1t=qZ6|+`PQS~o+xY@TD^N? z-u3Q(YSg){32(AihU^|zUGCz<(S0QnRY(40xtFGBP4ADPITeDi;qixCTOravYrx7=q_LJF9>AmPRf13EE_B)SzS{p=e){0sd^ z9x1(m`n%3$u(+Q{Kv=nwm{h$GbhbQa-3kk=(!`#=0vu1t%X<77qj74^(5TEJ{p^tY|!!R

kfC`>J3FpQ)`Y2bL` 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

9#RL%N^U`ZxT)zo(~^*FYd>|NEe1 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

+J&s+WzG10Vg@khrdibxwPxKRN!m(&t!^8$oN^@ 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

SY-!8*QlDj&!zm`bH4b_CKl$Wc^ARd&;* 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®YYjNX7zWSM;9R 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 vqOEs7DLZHKm{MK-jjbh&%lUfqHN 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 literal 0 HcmV?d00001 diff --git a/modules/common/development/debug-tools.nix b/modules/common/development/debug-tools.nix index 02250108e..61152eb25 100644 --- a/modules/common/development/debug-tools.nix +++ b/modules/common/development/debug-tools.nix @@ -5,45 +5,88 @@ lib, pkgs, ... -}: let +}: +let cfg = config.ghaf.development.debug.tools; + + rm-linux-bootmgrs = pkgs.callPackage ./scripts/rm_linux_bootmgr_entries.nix { }; + perf-test-script-icicle = pkgs.callPackage ./scripts/perf_test_icicle_kit.nix { }; + sysbench-test-script = pkgs.callPackage ./scripts/sysbench_test.nix { }; + sysbench-fileio-test-script = pkgs.callPackage ./scripts/sysbench_fileio_test.nix { }; + nvpmodel-check = pkgs.callPackage ./scripts/nvpmodel_check.nix { }; + + inherit (lib) mkEnableOption mkIf; + inherit (import ../../../lib/launcher.nix { inherit pkgs lib; }) rmDesktopEntries; in - with lib; { - options.ghaf.development.debug.tools = { - enable = mkEnableOption "Debug Tools"; - }; +{ + options.ghaf.development.debug.tools = { + enable = mkEnableOption "Debug Tools"; + }; - config = mkIf cfg.enable { - environment.systemPackages = with pkgs; - [ + config = mkIf cfg.enable { + environment.etc = { + audio_test.source = ./audio_test; + }; + environment.systemPackages = + builtins.attrValues { + inherit (pkgs) # For lspci: - pciutils + pciutils # For lsusb: - usbutils + usbutils # Useful in NetVM - ethtool + ethtool # Basic monitors - htop + iftop iotop - traceroute dig evtest + # For deleting Linux Boot Manager entries in automated testing + efibootmgr # Performance testing + speedtest-cli iperf - # Match perf version with kernel. - config.boot.kernelPackages.perf + tree + file + # to build ghaf on target + + git + + # Grpc testing + + grpcurl + ; + } + ++ + # Match perf version with kernel. + [ + #(config.boot.kernelPackages.perf.override {python3 = pkgs.python311;}) + sysbench-test-script + sysbench-fileio-test-script + nvpmodel-check + rm-linux-bootmgrs ] - # TODO Can this be changed to platformPkgs to filter ? - # LuaJIT (which is sysbench dependency) not available on RISC-V - ++ lib.optional (config.nixpkgs.hostPlatform.system != "riscv64-linux") sysbench - # runtimeShell (unixbench dependency) not available on RISC-V nor on cross-compiled Orin AGX/NX - ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform) unixbench; - }; - } + ++ rmDesktopEntries [ pkgs.htop ] + #TODO tmp disable perf as it is broken in cross-compiled Orin AGX/NX + ++ lib.optional ( + config.nixpkgs.hostPlatform.system != "aarch64-linux" + ) config.boot.kernelPackages.perf + # LuaJIT (which is sysbench dependency) not available on RISC-V + ++ lib.optional (config.nixpkgs.hostPlatform.system != "riscv64-linux") pkgs.sysbench + # Icicle Kit performance test script available on RISC-V + ++ lib.optional (config.nixpkgs.hostPlatform.system == "riscv64-linux") perf-test-script-icicle + # runtimeShell (unixbench dependency) not available on RISC-V nor on cross-compiled Orin AGX/NX + ++ lib.optional (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) pkgs.unixbench + # Build VLC only on x86 + ++ lib.optionals (config.nixpkgs.hostPlatform.system == "x86_64-linux") (rmDesktopEntries [ + pkgs.vlc + ]); + }; +} diff --git a/modules/common/development/nix.nix b/modules/common/development/nix.nix index 868919555..7b7f45ffe 100644 --- a/modules/common/development/nix.nix +++ b/modules/common/development/nix.nix @@ -1,39 +1,54 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - config, - lib, - ... -}: let +{ config, lib, ... }: +let cfg = config.ghaf.development.nix-setup; + inherit (lib) + mkEnableOption + mkOption + mkIf + types + ; in - with lib; { - options.ghaf.development.nix-setup = { - enable = mkEnableOption "Target Nix config options"; - nixpkgs = mkOption { - type = types.nullOr types.path; - default = null; - description = "Path to the nixpkgs repository"; - }; +{ + options.ghaf.development.nix-setup = { + enable = mkEnableOption "Target Nix config options"; + nixpkgs = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to the nixpkgs repository"; }; + }; - config = mkIf cfg.enable { - nix = { - settings = { - experimental-features = ["nix-command" "flakes"]; - keep-outputs = true; - keep-derivations = true; - }; + config = mkIf cfg.enable { + nix = { + settings = { + experimental-features = [ + "nix-command" + "flakes" + ]; + keep-outputs = true; + keep-derivations = true; + }; + + # avoid scenario where the host rootfs gets filled + # with nixos-rebuild ... switch generated excess + # generations and becomes unbootable + gc = { + automatic = true; + dates = "daily"; + options = "--delete-older-than 3d"; + }; - # Set the path and registry so that e.g. nix-shell and repl work - nixPath = lib.mkIf (cfg.nixpkgs != null) ["nixpkgs=${cfg.nixpkgs}"]; + # Set the path and registry so that e.g. nix-shell and repl work + nixPath = lib.mkIf (cfg.nixpkgs != null) [ "nixpkgs=${cfg.nixpkgs}" ]; - registry = lib.mkIf (cfg.nixpkgs != null) { - nixpkgs.to = { - type = "path"; - path = cfg.nixpkgs; - }; + registry = lib.mkIf (cfg.nixpkgs != null) { + nixpkgs.to = { + type = "path"; + path = cfg.nixpkgs; }; }; }; - } + }; +} diff --git a/modules/common/development/scripts/nvpmodel_check.nix b/modules/common/development/scripts/nvpmodel_check.nix new file mode 100644 index 000000000..8dbe82552 --- /dev/null +++ b/modules/common/development/scripts/nvpmodel_check.nix @@ -0,0 +1,37 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ writeShellApplication, lib, ... }: +writeShellApplication { + name = "nvpmodel-check"; + text = '' + # Since performance depends heavily on power mode it should be checked before performance testing. + + # This integer is given with the command, e.g. 'nvpmodel-check 3' + ExpectedPowerModeNo="''$1" + + if hostname | grep -qw "ghaf-host"; then + if nvpmodel | grep -q "command not found"; then + echo -e "nvpmodel not available\ŋ" + else + echo -e "''$(nvpmodel -q)\n" + ModeNo=''$(nvpmodel -q | awk -F: 'NR==2 {print ''$1}') + if [ "''$ModeNo" -eq "''$ExpectedPowerModeNo" ]; then + echo "Power mode check ok: ''${ModeNo}" + exit 0 + else + echo "Unexpected power mode detected: ''${ModeNo}" + fi + fi + else + echo -e "\nVirtual environment detected. Power mode cannot be checked." + fi + exit 1 + ''; + meta = with lib; { + description = " + Script for checking power mode of an Orin AGX/NX target. + If executed in correct environment (ghaf-host) it gives return code 0 when the power mode number is as expected. + Otherwise the return code is 1. + "; + }; +} diff --git a/modules/common/development/scripts/perf_test_icicle_kit.nix b/modules/common/development/scripts/perf_test_icicle_kit.nix new file mode 100644 index 000000000..595206dd8 --- /dev/null +++ b/modules/common/development/scripts/perf_test_icicle_kit.nix @@ -0,0 +1,25 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ writeShellApplication, lib, ... }: +writeShellApplication { + name = "perf-test-icicle-kit"; + text = '' + time { + perf bench sched messaging; + perf bench sched pipe -l 50000; + perf bench syscall basic; + perf bench mem memcpy; + perf bench mem memset; + perf bench mem find_bit -i 5 -j 1000; + perf bench numa mem -p 1 -t 1 -P 1024 -C 0 -M 0 -s 5 -zZq --thp 1 --no-data_rand_walk; + perf bench futex all; + perf bench epoll wait; + perf bench epoll ctl; + perf bench internals synthesize -i 1000; + perf bench internals kallsyms-parse -i 10; + } | tee -a perf_results_YYYY-MM-DD_BUILDER-BuildID_SDorEMMC + ''; + meta = with lib; { + description = "Perf test script customized for measuring ghaf performance on Microchip Icicle Kit target"; + }; +} diff --git a/modules/common/development/scripts/rm_linux_bootmgr_entries.nix b/modules/common/development/scripts/rm_linux_bootmgr_entries.nix new file mode 100644 index 000000000..8aab934fb --- /dev/null +++ b/modules/common/development/scripts/rm_linux_bootmgr_entries.nix @@ -0,0 +1,15 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ writeShellApplication, lib, ... }: +writeShellApplication { + name = "rm-linux-bootmgrs"; + text = '' + for id in ''$(efibootmgr | grep Linux | awk 'NR > 0 {print ''$1}' | cut -c 5-8) + do + sudo efibootmgr -q -b "''${id}" -B + done + ''; + meta = with lib; { + description = "Helper script for removing all Linux Boot Manager entries from UEFI Boot order list"; + }; +} diff --git a/modules/common/development/scripts/sysbench_fileio_test.nix b/modules/common/development/scripts/sysbench_fileio_test.nix new file mode 100755 index 000000000..805b3ee54 --- /dev/null +++ b/modules/common/development/scripts/sysbench_fileio_test.nix @@ -0,0 +1,49 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ writeShellApplication, lib, ... }: +writeShellApplication { + name = "sysbench-fileio-test"; + text = '' + # Test set to be run with sysbench + + # These variable needs to be given on the command line. For example: sysbench-fileio-test 20 + THREADS="''$1" + + # Create a directory for the results + RESULT_DIR="sysbench_results" + echo -e "\nCreating directory for test results:\n./''$RESULT_DIR" + mkdir -p ''$RESULT_DIR + + # Create test_info file with system information + echo -e "\nSaving information about test environment to ./''$RESULT_DIR/test_info\n" + echo -e "''$(lscpu)" "\n\n" "''$(free)" "\n\n" "''$(df)" "\n\n" >> ./''$RESULT_DIR/test_info + echo -e "\nHost: ''$(hostname)\n" | tee -a ./''$RESULT_DIR/test_info + + # Calculate total memory in kB and set FILE_TOTAL_SIZE 4GB higher than the total memory + TOTAL_MEM_kB=''$(free | awk -F: 'NR==2 {print ''$2}' | awk '{print ''$1}') + FILE_TOTAL_SIZE_kB=''$((TOTAL_MEM_kB + 4000000)) + + # Read available disk space in kB and check for sufficient disk space + AVAILABLE_DISK_SPACE_kB=''$(df | grep -w "/" | awk '{print ''$4}') + if [ ''$((FILE_TOTAL_SIZE_kB + FILE_TOTAL_SIZE_kB / 10)) -gt "''$AVAILABLE_DISK_SPACE_kB" ]; then + echo -e "\nInsufficient disk space for fileio test." | tee -a ./''$RESULT_DIR/test_info + exit 1 + fi + + echo -e "\nDetected available total memory ''${TOTAL_MEM_kB} kB." | tee -a ./''$RESULT_DIR/test_info + echo -e "\nDetected available disk space ''${AVAILABLE_DISK_SPACE_kB} kB." | tee -a ./''$RESULT_DIR/test_info + echo -e "\nStarting fileio test with FILE_TOTAL_SIZE=''${FILE_TOTAL_SIZE_kB} kB." | tee -a ./''$RESULT_DIR/test_info + + # Execute sysbench fileio tests if the checks passed + sysbench fileio --file-total-size=''${FILE_TOTAL_SIZE_kB}K --threads="''${THREADS}" --file-test-mode=seqrd prepare + sysbench fileio --file-total-size=''${FILE_TOTAL_SIZE_kB}K --threads="''${THREADS}" --file-test-mode=seqrd --time=30 run | tee ./''$RESULT_DIR/fileio_rd_report + sysbench fileio cleanup + sysbench fileio --file-total-size=''${FILE_TOTAL_SIZE_kB}K --threads="''${THREADS}" --file-test-mode=seqwr --time=30 run | tee ./''$RESULT_DIR/fileio_wr_report + sysbench fileio cleanup + + echo -e "\nTest finished.\n" + ''; + meta = with lib; { + description = "Script for sysbench fileio tests"; + }; +} diff --git a/modules/common/development/scripts/sysbench_test.nix b/modules/common/development/scripts/sysbench_test.nix new file mode 100755 index 000000000..e28401d34 --- /dev/null +++ b/modules/common/development/scripts/sysbench_test.nix @@ -0,0 +1,46 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ writeShellApplication, lib, ... }: +writeShellApplication { + name = "sysbench-test"; + text = '' + # Threads variable needs to be given on the command line. + # For example: ./sysbench_simplified_test 20 + THREADS="''$1" + + # Create a directory for the results with a timestamp + RESULT_DIR="sysbench_results" + echo -e "\nCreating directory for test results:" + echo "./''$RESULT_DIR" + mkdir -p ''$RESULT_DIR + + # Create test_info file with information about the run environment: lscpu, free, df + echo -e "\nSaving information about test environment to ./''$RESULT_DIR/test_info\n" + echo -e "''$(lscpu)" "\n\n" "''$(free)" "\n\n" "''$(df)" "\n\n" >> ./''$RESULT_DIR/test_info + + echo -e "\nHost: ''$(hostname)\n" | tee -a ./''$RESULT_DIR/test_info + + # cpu tests + echo -e "\nRunning CPU tests...\n" + sysbench cpu --time=10 --threads=1 --cpu-max-prime=20000 run | tee ./''$RESULT_DIR/cpu_1thread_report + if [ "''$THREADS" -gt 1 ] + then + sysbench cpu --time=10 --threads="''${THREADS}" --cpu-max-prime=20000 run | tee ./''$RESULT_DIR/cpu_report + fi + + # memory tests + echo -e "\nRunning memory tests...\n" + sysbench memory --time=60 --memory-oper=read --threads=1 run | tee ./''$RESULT_DIR/memory_read_1thread_report + sysbench memory --time=60 --memory-oper=write --threads=1 run | tee ./''$RESULT_DIR/memory_write_1thread_report + if [ "''$THREADS" -gt 1 ] + then + sysbench memory --time=15 --memory-oper=read --threads="''${THREADS}" run | tee ./''$RESULT_DIR/memory_read_report + sysbench memory --time=30 --memory-oper=write --threads="''${THREADS}" run | tee ./''$RESULT_DIR/memory_write_report + fi + + echo -e "\nTest finished.\n" + ''; + meta = with lib; { + description = "Script for sysbench tests (excluding fileio)"; + }; +} diff --git a/modules/common/development/ssh.nix b/modules/common/development/ssh.nix index 3bb921f00..923e27897 100644 --- a/modules/common/development/ssh.nix +++ b/modules/common/development/ssh.nix @@ -1,24 +1,19 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - config, - lib, - ... -}: let +{ config, lib, ... }: +let cfg = config.ghaf.development.ssh.daemon; - inherit ((import ./authorized_ssh_keys.nix)) authorizedKeys; + inherit (lib) mkEnableOption mkIf; in - with lib; { - options.ghaf.development.ssh.daemon = { - enable = mkEnableOption "ssh daemon"; - }; +{ + options.ghaf.development.ssh.daemon = { + enable = mkEnableOption "ssh daemon"; + }; - config = mkIf cfg.enable { - services.openssh = { - enable = true; - settings.X11Forwarding = true; - }; - users.users.root.openssh.authorizedKeys.keys = authorizedKeys; - users.users.${config.ghaf.users.accounts.user}.openssh.authorizedKeys.keys = authorizedKeys; - }; - } + config = mkIf cfg.enable { + services.openssh = { + enable = true; + settings.X11Forwarding = true; + }; + }; +} diff --git a/modules/common/development/usb-serial.nix b/modules/common/development/usb-serial.nix index 6be47c754..c19db3a70 100644 --- a/modules/common/development/usb-serial.nix +++ b/modules/common/development/usb-serial.nix @@ -1,25 +1,23 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - config, - lib, - ... -}: let +{ config, lib, ... }: +let cfg = config.ghaf.development.usb-serial; + inherit (lib) mkEnableOption mkIf; in - with lib; { - options.ghaf.development.usb-serial = { - enable = mkEnableOption "Usb-Serial"; - }; +{ + options.ghaf.development.usb-serial = { + enable = mkEnableOption "Usb-Serial"; + }; - #TODO Should this be alos bound to only x86? - config = mkIf cfg.enable { - services.getty.extraArgs = ["115200"]; - systemd.services."autovt@ttyUSB0".enable = true; + #TODO Should this be alos bound to only x86? + config = mkIf cfg.enable { + services.getty.extraArgs = [ "115200" ]; + systemd.services."autovt@ttyUSB0".enable = true; - # ttyUSB0 service is active as soon as corresponding device appears - services.udev.extraRules = '' - SUBSYSTEM=="tty", KERNEL=="ttyUSB0", TAG+="systemd", ENV{SYSTEMD_WANTS}+="autovt@ttyUSB0.service" - ''; - }; - } + # ttyUSB0 service is active as soon as corresponding device appears + services.udev.extraRules = '' + SUBSYSTEM=="tty", KERNEL=="ttyUSB0", TAG+="systemd", ENV{SYSTEMD_WANTS}+="autovt@ttyUSB0.service" + ''; + }; +} diff --git a/modules/common/firewall/default.nix b/modules/common/firewall/default.nix index d13e33b8f..9d28c039a 100644 --- a/modules/common/firewall/default.nix +++ b/modules/common/firewall/default.nix @@ -3,8 +3,4 @@ # # Firewall related modules # -{ - imports = [ - ./kernel-modules.nix - ]; -} +{ imports = [ ./kernel-modules.nix ]; } diff --git a/modules/common/firewall/kernel-modules.nix b/modules/common/firewall/kernel-modules.nix index 2a72d8078..79aafe0b7 100644 --- a/modules/common/firewall/kernel-modules.nix +++ b/modules/common/firewall/kernel-modules.nix @@ -6,13 +6,11 @@ # Adds bunch of modules to the kernel, so firewall can start, as our custom # kernels don't seem to always have all necessary modules enabled. # -{ - config, - lib, - ... -}: let +{ config, lib, ... }: +let cfg = config.ghaf.firewall.kernel-modules; -in { +in +{ options.ghaf.firewall.kernel-modules = { enable = lib.mkEnableOption "kernel modules required for firewall"; }; diff --git a/modules/common/hardware/ax88179_178a.nix b/modules/common/hardware/ax88179_178a.nix deleted file mode 100644 index 3e0f686b5..000000000 --- a/modules/common/hardware/ax88179_178a.nix +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# Fix for ax88179_178a USB network card kernel driver MAC-address issue. -{ - lib, - pkgs, - config, - ... -}: let - cfg = config.ghaf.hardware.ax88179_178a; -in { - options.ghaf.hardware.ax88179_178a = { - enable = lib.mkEnableOption "fix for ax88179_178a USB network card kernel driver MAC-address"; - }; - - config = lib.mkIf cfg.enable { - boot.kernelPatches = [ - # Fix MAC-address randomized on USB network cards because of kernel bug. - # This specifically affects network cards used in testing. - { - patch = pkgs.fetchpatch2 { - url = "https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/patch/?id=2e91bb99b9d4f756e92e83c4453f894dda220f09"; - hash = "sha256-fX7yBsXW1oFt1Nfns42oZnCXf36qehXijvCNEmqBGsE="; - }; - } - ]; - }; -} diff --git a/modules/common/hardware/definition.nix b/modules/common/hardware/definition.nix deleted file mode 100644 index 2e99fdfb5..000000000 --- a/modules/common/hardware/definition.nix +++ /dev/null @@ -1,143 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# Module for Hardware Definitions -# -# The point of this module is to only store information about the hardware -# configuration, and the logic that uses this information should be elsewhere. -{lib, ...}: { - options.ghaf.hardware.definition = with lib; let - pciDevSubmodule = types.submodule { - options = { - path = mkOption { - type = types.str; - description = '' - PCI device path - ''; - }; - vendorId = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - PCI Vendor ID (optional) - ''; - }; - productId = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - PCI Product ID (optional) - ''; - }; - name = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - PCI device name (optional) - ''; - }; - }; - }; - in { - name = mkOption { - description = "Name of the hardware"; - type = types.str; - default = ""; - }; - - mouse = mkOption { - description = "Name of the mouse device(s)"; - type = types.listOf types.str; - default = []; - }; - - touchpad = mkOption { - description = "Name of the touchpad device(s)"; - type = types.listOf types.str; - default = []; - }; - - network = { - # TODO? Should add NetVM enabler here? - # netvm.enable = mkEnableOption = "NetVM"; - - pciDevices = mkOption { - description = "PCI Devices to passthrough to NetVM"; - type = types.listOf pciDevSubmodule; - default = []; - example = literalExpression '' - [{ - path = "0000:00:14.3"; - vendorId = "8086"; - productId = "51f1"; - }] - ''; - }; - }; - - disks = mkOption { - description = "Disks to format and mount"; - type = types.attrsOf (types.submodule { - options.device = mkOption { - type = types.str; - description = '' - Path to the disk - ''; - }; - }); - default = {}; - example = literalExpression '' - { - disk1.device = "/dev/nvme0n1"; - } - ''; - }; - - gpu = { - # TODO? Should add GuiVM enabler here? - # guivm.enable = mkEnableOption = "NetVM"; - - pciDevices = mkOption { - description = "PCI Devices to passthrough to GuiVM"; - type = types.listOf pciDevSubmodule; - default = []; - example = literalExpression '' - [{ - path = "0000:00:02.0"; - vendorId = "8086"; - productId = "a7a1"; - }] - ''; - }; - }; - - virtioInputHostEvdevs = mkOption { - description = '' - List of input device files to passthrough to GuiVM using - "-device virtio-input-host-pci,evdev=" QEMU command line argument. - ''; - type = types.listOf types.str; - default = []; - example = literalExpression '' - [ - "evdev=/dev/input/by-path/platform-i8042-serio-0-event-kbd" - "evdev=/dev/mouse" - "evdev=/dev/touchpad" - "evdev=/dev/input/by-path/platform-i8042-serio-1-event-mouse" - ] - ''; - }; - - udevRules = mkOption { - description = '' - Definition of required udev rules. - ''; - type = types.str; - default = ""; - example = literalExpression '' - # Laptop keyboard - SUBSYSTEM=="input",ATTRS{name}=="AT Translated Set 2 keyboard",GROUP="kvm" - ''; - }; - }; -} diff --git a/modules/common/hardware/lenovo-x1/definitions/default.nix b/modules/common/hardware/lenovo-x1/definitions/default.nix deleted file mode 100644 index 8b28c75aa..000000000 --- a/modules/common/hardware/lenovo-x1/definitions/default.nix +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -{ - generation, - lib, -}: let - hwDefinition = import (./. + "/x1-${generation}.nix"); -in { - inherit (hwDefinition) mouse; - inherit (hwDefinition) touchpad; - inherit (hwDefinition) disks; - inherit (hwDefinition) network; - inherit (hwDefinition) gpu; - - # Notes: - # 1. This assembles udev rules for different hw configurations (i.e., different mice/touchpads) by adding - # all of them to the configuration. This was chosen for simplicity to not have to provide hw identifier at build, - # but is not ideal and should be changed. - # 2. USB camera "passthrough" is handled by qemu and thus available on host. If peripheral VM is implemented, - # the entire host controller should be passthrough'd using the PCI bus (14.0). In x1, bluetooth and fingerprint - # reader are on this bus. - udevRules = let - mapMouseRules = - builtins.map (d: '' SUBSYSTEM=="input", ATTRS{name}=="${d}", KERNEL=="event*", GROUP="kvm", SYMLINK+="mouse" - ''); - mapTouchpadRules = - builtins.map (d: '' SUBSYSTEM=="input", ATTRS{name}=="${d}", KERNEL=="event*", GROUP="kvm", SYMLINK+="touchpad" - ''); - in '' - # Laptop keyboard - SUBSYSTEM=="input", ATTRS{name}=="AT Translated Set 2 keyboard", GROUP="kvm" - # Laptop TrackPoint - SUBSYSTEM=="input", ATTRS{name}=="TPPS/2 Elan TrackPoint", GROUP="kvm" - # Lenovo X1 integrated webcam - KERNEL=="3-8", SUBSYSTEM=="usb", ATTR{busnum}=="3", ATTR{devnum}=="3", GROUP="kvm" - # Mouse and Touchpad - ${lib.strings.concatStrings (mapMouseRules hwDefinition.mouse)} - ${lib.strings.concatStrings (mapTouchpadRules hwDefinition.touchpad)} - ''; - - virtioInputHostEvdevs = [ - # Lenovo X1 touchpad and keyboard - "/dev/input/by-path/platform-i8042-serio-0-event-kbd" - "/dev/mouse" - "/dev/touchpad" - # Lenovo X1 trackpoint (red button/joystick) - "/dev/input/by-path/platform-i8042-serio-1-event-mouse" - ]; -} diff --git a/modules/common/hardware/lenovo-x1/definitions/x1-gen10.nix b/modules/common/hardware/lenovo-x1/definitions/x1-gen10.nix deleted file mode 100644 index 8cfc01b10..000000000 --- a/modules/common/hardware/lenovo-x1/definitions/x1-gen10.nix +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -{ - name = "Lenovo X1 Carbon Gen 10"; - - mouse = ["ELAN067B:00 04F3:31F8 Mouse" "SYNA8016:00 06CB:CEB3 Mouse"]; - touchpad = ["ELAN067B:00 04F3:31F8 Touchpad" "SYNA8016:00 06CB:CEB3 Touchpad"]; - - disks = { - disk1.device = "/dev/nvme0n1"; - }; - - network.pciDevices = [ - { - # Passthrough Intel WiFi card - path = "0000:00:14.3"; - vendorId = "8086"; - productId = "51f0"; - name = "wlp0s5f0"; - } - ]; - - gpu.pciDevices = [ - { - # Passthrough Intel Iris GPU - path = "0000:00:02.0"; - vendorId = "8086"; - productId = "46a6"; - } - ]; -} diff --git a/modules/common/hardware/lenovo-x1/definitions/x1-gen11.nix b/modules/common/hardware/lenovo-x1/definitions/x1-gen11.nix deleted file mode 100644 index 72818febd..000000000 --- a/modules/common/hardware/lenovo-x1/definitions/x1-gen11.nix +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -{ - name = "Lenovo X1 Carbon Gen 11"; - - mouse = [ - "ELAN067C:00 04F3:31F9 Mouse" - "SYNA8016:00 06CB:CEB3 Mouse" - "ELAN067B:00 04F3:31F8 Mouse" - ]; - - touchpad = [ - "ELAN067C:00 04F3:31F9 Touchpad" - "SYNA8016:00 06CB:CEB3 Touchpad" - "ELAN067B:00 04F3:31F8 Touchpad" - ]; - - disks = { - disk1.device = "/dev/nvme0n1"; - }; - - network.pciDevices = [ - { - # Passthrough Intel WiFi card - path = "0000:00:14.3"; - vendorId = "8086"; - productId = "51f1"; - name = "wlp0s5f0"; - } - ]; - - gpu.pciDevices = [ - { - # Passthrough Intel Iris GPU - path = "0000:00:02.0"; - vendorId = "8086"; - productId = "a7a1"; - } - ]; -} diff --git a/modules/common/hardware/lenovo-x1/kernel/guest/test/default.nix b/modules/common/hardware/lenovo-x1/kernel/guest/test/default.nix deleted file mode 100644 index 288fd2e55..000000000 --- a/modules/common/hardware/lenovo-x1/kernel/guest/test/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{pkgs, ...}: let - config = pkgs.nixos [./test-configuration.nix]; -in - config.config.system.build.toplevel diff --git a/modules/common/hardware/lenovo-x1/kernel/guest/test/test-configuration.nix b/modules/common/hardware/lenovo-x1/kernel/guest/test/test-configuration.nix deleted file mode 100644 index cbb6e4694..000000000 --- a/modules/common/hardware/lenovo-x1/kernel/guest/test/test-configuration.nix +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - config, - lib, - ... -}: { - imports = [ - ../../../../x86_64-generic/kernel/host/default.nix - ../../../../x86_64-generic/kernel/guest/default.nix - ]; - - # baseline, virtualization and network hardening are - # generic to all x86_64 devices - config.ghaf.host.kernel.hardening.enable = true; - config.ghaf.host.kernel.hardening.virtualization.enable = true; - config.ghaf.host.kernel.hardening.networking.enable = true; - config.ghaf.host.kernel.hardening.inputdevices.enable = true; - # usb/debug hardening is host optional but required for -debug builds - config.ghaf.host.kernel.hardening.usb.enable = true; - config.ghaf.host.kernel.hardening.debug.enable = true; - - # guest VM kernel specific options - config.ghaf.guest.kernel.hardening.enable = true; - config.ghaf.guest.kernel.hardening.graphics.enable = true; - - # required to module test a module via top level configuration - config.boot.loader.systemd-boot.enable = true; - config.fileSystems."/" = { - device = "/dev/disk/by-uuid/00000000-0000-0000-0000-000000000000"; - fsType = "ext4"; - }; - config.system.stateVersion = lib.trivial.release; -} diff --git a/modules/common/hardware/x86_64-generic/kernel/guest/default.nix b/modules/common/hardware/x86_64-generic/kernel/guest/default.nix deleted file mode 100644 index 9d11d5288..000000000 --- a/modules/common/hardware/x86_64-generic/kernel/guest/default.nix +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{lib, ...}: -with lib; { - options.ghaf.guest.kernel.hardening.enable = mkOption { - description = "Enable Ghaf Guest hardening feature"; - type = types.bool; - default = false; - }; - options.ghaf.guest.kernel.hardening.graphics.enable = mkOption { - description = "Enable support for Graphics in the Ghaf Guest"; - type = types.bool; - default = false; - }; -} diff --git a/modules/common/hardware/x86_64-generic/kernel/hardening.nix b/modules/common/hardware/x86_64-generic/kernel/hardening.nix deleted file mode 100644 index d1d61b1b9..000000000 --- a/modules/common/hardware/x86_64-generic/kernel/hardening.nix +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{...}: { - imports = [ - ./host - ./guest - ./host/pkvm - # other host hardening modules - to be defined later - ]; - - config = { - # host kernel hardening - ghaf.host.kernel.hardening.enable = false; - ghaf.host.kernel.hardening.virtualization.enable = false; - ghaf.host.kernel.hardening.networking.enable = false; - ghaf.host.kernel.hardening.usb.enable = false; - ghaf.host.kernel.hardening.inputdevices.enable = false; - ghaf.host.kernel.hardening.debug.enable = false; - # host kernel hypervisor (KVM) hardening - ghaf.host.kernel.hardening.hypervisor.enable = false; - # guest kernel hardening - ghaf.guest.kernel.hardening.enable = false; - ghaf.guest.kernel.hardening.graphics.enable = false; - # other host hardening options - user space, etc. - to be defined later - }; -} diff --git a/modules/common/hardware/x86_64-generic/kernel/host/pkvm/test/default.nix b/modules/common/hardware/x86_64-generic/kernel/host/pkvm/test/default.nix deleted file mode 100644 index 288fd2e55..000000000 --- a/modules/common/hardware/x86_64-generic/kernel/host/pkvm/test/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{pkgs, ...}: let - config = pkgs.nixos [./test-configuration.nix]; -in - config.config.system.build.toplevel diff --git a/modules/common/hardware/x86_64-generic/kernel/host/pkvm/test/test-configuration.nix b/modules/common/hardware/x86_64-generic/kernel/host/pkvm/test/test-configuration.nix deleted file mode 100644 index 6dfb78431..000000000 --- a/modules/common/hardware/x86_64-generic/kernel/host/pkvm/test/test-configuration.nix +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - config, - lib, - ... -}: { - imports = [ - ../default.nix - ]; - - # pkvm hardening is generic to all x86_64 devices - config.ghaf.host.kernel.hardening.hypervisor.enable = true; - - # required to module test a module via top level configuration - config.boot.loader.systemd-boot.enable = true; - config.fileSystems."/" = { - device = "/dev/disk/by-uuid/00000000-0000-0000-0000-000000000000"; - fsType = "ext4"; - }; - config.system.stateVersion = lib.trivial.release; -} diff --git a/modules/common/hardware/x86_64-generic/kernel/host/test/default.nix b/modules/common/hardware/x86_64-generic/kernel/host/test/default.nix deleted file mode 100644 index 288fd2e55..000000000 --- a/modules/common/hardware/x86_64-generic/kernel/host/test/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{pkgs, ...}: let - config = pkgs.nixos [./test-configuration.nix]; -in - config.config.system.build.toplevel diff --git a/modules/common/hardware/x86_64-generic/kernel/host/test/test-configuration.nix b/modules/common/hardware/x86_64-generic/kernel/host/test/test-configuration.nix deleted file mode 100644 index 7b0688dfe..000000000 --- a/modules/common/hardware/x86_64-generic/kernel/host/test/test-configuration.nix +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - config, - lib, - ... -}: { - imports = [ - ../default.nix - # import guest also to bring the defaults (false) to scope - ../../guest/default.nix - ]; - - # baseline, virtualization and network hardening are - # generic to all x86_64 devices - config.ghaf.host.kernel.hardening.enable = true; - config.ghaf.host.kernel.hardening.virtualization.enable = true; - config.ghaf.host.kernel.hardening.networking.enable = true; - config.ghaf.host.kernel.hardening.inputdevices.enable = true; - # usb/debug hardening is host optional but required for -debug builds - config.ghaf.host.kernel.hardening.usb.enable = true; - config.ghaf.host.kernel.hardening.debug.enable = true; - - # required to module test a module via top level configuration - config.boot.loader.systemd-boot.enable = true; - config.fileSystems."/" = { - device = "/dev/disk/by-uuid/00000000-0000-0000-0000-000000000000"; - fsType = "ext4"; - }; - config.system.stateVersion = lib.trivial.release; -} diff --git a/modules/common/hardware/x86_64-linux.nix b/modules/common/hardware/x86_64-linux.nix deleted file mode 100644 index 28c356f60..000000000 --- a/modules/common/hardware/x86_64-linux.nix +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - config, - lib, - ... -}: let - cfg = config.ghaf.hardware.x86_64.common; -in - with lib; { - options.ghaf.hardware.x86_64.common = { - enable = mkEnableOption "Common x86 configs"; - }; - - config = mkIf cfg.enable { - nixpkgs.hostPlatform.system = "x86_64-linux"; - - # Increase the support for different devices by allowing the use - # of proprietary drivers from the respective vendors - nixpkgs.config.allowUnfree = true; - - # Add this for x86_64 hosts to be able to more generically support hardware. - # For example Intel NUC 11's graphics card needs this in order to be able to - # properly provide acceleration. - hardware.enableRedistributableFirmware = true; - hardware.enableAllFirmware = true; - - boot = { - # Enable normal Linux console on the display - kernelParams = ["console=tty0"]; - - # To enable installation of ghaf into NVMe drives - initrd.availableKernelModules = [ - "nvme" - "uas" - ]; - loader = { - efi.canTouchEfiVariables = true; - systemd-boot.enable = true; - }; - }; - }; - } diff --git a/modules/common/logging/client.nix b/modules/common/logging/client.nix new file mode 100644 index 000000000..184d2ebed --- /dev/null +++ b/modules/common/logging/client.nix @@ -0,0 +1,51 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.logging.client; + endpointUrl = config.ghaf.logging.client.endpoint; +in +{ + options.ghaf.logging.client.endpoint = lib.mkOption { + description = '' + Assign endpoint url value to the alloy.service running in + different log producers. This endpoint URL will include + protocol, upstream, address along with port value. + ''; + type = lib.types.str; + }; + + config = lib.mkIf cfg.enable { + environment.etc."alloy/client.alloy" = { + text = '' + discovery.relabel "journal" { + targets = [] + rule { + source_labels = ["__journal__hostname"] + target_label = "nodename" + } + } + + loki.source.journal "journal" { + path = "/var/log/journal" + relabel_rules = discovery.relabel.journal.rules + forward_to = [loki.write.adminvm.receiver] + } + + loki.write "adminvm" { + endpoint { + url = "${endpointUrl}" + } + } + ''; + # The UNIX file mode bits + mode = "0644"; + }; + + services.alloy.enable = true; + # Once alloy.service in admin-vm stopped this service will + # still keep on retrying to send logs batch, so we need to + # stop it forcefully. + systemd.services.alloy.serviceConfig.TimeoutStopSec = 4; + }; +} diff --git a/modules/common/logging/default.nix b/modules/common/logging/default.nix new file mode 100644 index 000000000..4cab0fca7 --- /dev/null +++ b/modules/common/logging/default.nix @@ -0,0 +1,40 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + # Creating logging configuration options needed across the host and vms + options.ghaf.logging = { + client.enable = mkOption { + description = '' + Enable logging client service. Currently we have grafana alloy + running as client which will upload system journal logs to + grafana alloy running in admin-vm. + ''; + type = types.bool; + default = false; + }; + + listener.address = mkOption { + description = '' + Listener address will be used where log producers will + push logs and where admin-vm alloy.service will be + keep on listening or receiving logs. + ''; + type = types.str; + }; + + listener.port = mkOption { + description = '' + Listener port for the logproto endpoint which will be + used to receive logs from different log producers. + Also this port value will be used to open the port in + the admin-vm firewall. + ''; + type = types.port; + default = 9999; + }; + }; +} diff --git a/modules/common/logging/hw-mac-retrieve.nix b/modules/common/logging/hw-mac-retrieve.nix new file mode 100644 index 000000000..82cc8863c --- /dev/null +++ b/modules/common/logging/hw-mac-retrieve.nix @@ -0,0 +1,46 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: +let + # TODO: replace sshCommand and MacCommand with givc rpc to retrieve Mac Address + sshCommand = "${pkgs.sshpass}/bin/sshpass -p ghaf ${pkgs.openssh}/bin/ssh -o StrictHostKeyChecking=no ghaf@net-vm"; + macCommand = "${pkgs.hwinfo}/bin/hwinfo --network --only /class/net/wlp0s5f0 | ${pkgs.gawk}/bin/awk '/Permanent HW Address/ {print $4}'"; + macAddressPath = config.ghaf.logging.identifierFilePath; +in +{ + options.ghaf.logging.identifierFilePath = lib.mkOption { + description = '' + This configuration option used to specify the identifier file path. + The identifier file will be text file which have unique identification + value per machine so that when logs will be uploaded to cloud + we can identify its origin. + ''; + type = lib.types.path; + example = "/tmp/MACAddress"; + }; + + config = lib.mkIf config.ghaf.logging.client.enable { + # TODO: Remove hw-mac.service and replace with givc rpc later + systemd.services."hw-mac" = { + description = "Retrieve MAC address from net-vm"; + wantedBy = [ "alloy.service" ]; + requires = [ "network-online.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + # Make sure we can ssh before we retrieve mac address + ExecStartPre = "${sshCommand} ls"; + ExecStart = '' + ${pkgs.bash}/bin/bash -c "echo -n $(${sshCommand} ${macCommand}) > ${macAddressPath}" + ''; + Restart = "on-failure"; + RestartSec = "1"; + }; + }; + }; +} diff --git a/modules/common/logging/logs-aggregator.nix b/modules/common/logging/logs-aggregator.nix new file mode 100644 index 000000000..37874cb23 --- /dev/null +++ b/modules/common/logging/logs-aggregator.nix @@ -0,0 +1,85 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + endpointUrl = config.ghaf.logging.server.endpoint; + listenerAddress = config.ghaf.logging.listener.address; + listenerPort = toString config.ghaf.logging.listener.port; + macAddressPath = config.ghaf.logging.identifierFilePath; +in +{ + options.ghaf.logging.server.endpoint = lib.mkOption { + description = '' + Assign endpoint url value to the alloy.service running in + admin-vm. This endpoint URL will include protocol, upstream + address along with port value. + ''; + type = lib.types.str; + }; + + config = lib.mkIf config.ghaf.logging.client.enable { + environment.etc."loki/pass" = { + text = "ghaf"; + }; + environment.etc."alloy/logs-aggregator.alloy" = { + text = '' + local.file "macAddress" { + // Alloy service can read file in this specific location + filename = "${macAddressPath}" + } + discovery.relabel "adminJournal" { + targets = [] + rule { + source_labels = ["__journal__hostname"] + target_label = "nodename" + } + } + + loki.source.journal "journal" { + path = "/var/log/journal" + relabel_rules = discovery.relabel.adminJournal.rules + forward_to = [loki.write.remote.receiver] + } + + loki.write "remote" { + endpoint { + url = "${endpointUrl}" + // TODO: To be replaced with stronger authentication method + basic_auth { + username = "ghaf" + password_file = "/etc/loki/pass" + } + } + // Write Ahead Log records incoming data and stores it on the local file + // system in order to guarantee persistence of acknowledged data. + wal { + enabled = true + max_segment_age = "240h" + drain_timeout = "4s" + } + external_labels = {systemdJournalLogs = local.file.macAddress.content } + } + + loki.source.api "listener" { + http { + listen_address = "${listenerAddress}" + listen_port = ${listenerPort} + } + + forward_to = [ + loki.write.remote.receiver, + ] + } + ''; + # The UNIX file mode bits + mode = "0644"; + }; + + services.alloy.enable = true; + systemd.services.alloy.serviceConfig.after = [ "hw-mac.service" ]; + # If there is no internet connection , shutdown/reboot will take around 100sec + # So, to fix that problem we need to add stop timeout + # https://github.com/grafana/loki/issues/6533 + systemd.services.alloy.serviceConfig.TimeoutStopSec = 4; + }; +} diff --git a/targets/lenovo-x1/releaseModules.nix b/modules/common/networking/default.nix similarity index 63% rename from targets/lenovo-x1/releaseModules.nix rename to modules/common/networking/default.nix index c2ee28a71..d488e28fb 100644 --- a/targets/lenovo-x1/releaseModules.nix +++ b/modules/common/networking/default.nix @@ -1,8 +1,3 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -# -[ - { - ghaf.profiles.release.enable = true; - } -] +{ imports = [ ./hosts.nix ]; } diff --git a/modules/common/networking/hosts.nix b/modules/common/networking/hosts.nix new file mode 100644 index 000000000..5b62e6d9e --- /dev/null +++ b/modules/common/networking/hosts.nix @@ -0,0 +1,133 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.networking.hosts; + inherit (lib) + mkIf + types + mkOption + optionals + ; + + hostsEntrySubmodule = types.submodule { + options = { + name = mkOption { + type = types.str; + description = '' + Host name as string. + ''; + }; + ip = mkOption { + type = types.str; + description = '' + Host IPv4 address as string. + ''; + }; + }; + }; + + # please note that .100. network is not + # reachable from ghaf-host. It's only reachable + # guest-to-guest. + # Use to .101. (debug) to access guests from host. + # debug network hosts are post-fixed: -debug + ipBase = "192.168.100"; + debugBase = "192.168.101"; + hostsEntries = [ + { + ip = 1; + name = "net-vm"; + } + { + ip = 2; + name = "ghaf-host"; + } + { + ip = 3; + name = "gui-vm"; + } + { + ip = 4; + name = "ids-vm"; + } + { + ip = 5; + name = "audio-vm"; + } + { + ip = 10; + name = "admin-vm"; + } + { + ip = 100; + name = "chromium-vm"; + } + { + ip = 101; + name = "gala-vm"; + } + { + ip = 102; + name = "zathura-vm"; + } + { + ip = 103; + name = "comms-vm"; + } + { + ip = 104; + name = "appflowy-vm"; + } + { + ip = 105; + name = "business-vm"; + } + ]; + + mkHostEntryTxt = + { ip, name }: + "${ipBase}.${toString ip}\t${name}\n" + + lib.optionalString config.ghaf.profiles.debug.enable "${debugBase}.${toString ip}\t${name}-debug\n"; + entriesTxt = map mkHostEntryTxt hostsEntries; + + mkHostEntry = + { ip, name }: + { + name = "${name}"; + ip = "${ipBase}.${toString ip}"; + }; + mkHostEntryDebug = + { ip, name }: + { + name = "${name}-debug"; + ip = "${debugBase}.${toString ip}"; + }; + entries = + (map mkHostEntry hostsEntries) + ++ optionals config.ghaf.profiles.debug.enable (map mkHostEntryDebug hostsEntries); +in +{ + options.ghaf.networking.hosts = { + enable = mkOption { + type = types.bool; + default = true; + }; + entries = mkOption { + type = types.listOf hostsEntrySubmodule; + default = null; + }; + }; + + config = mkIf cfg.enable { + ghaf.networking.hosts = { + inherit entries; + }; + + # Generate hosts file + environment.etc.hosts = lib.mkForce { + text = lib.foldl' (acc: x: acc + x) "127.0.0.1 localhost\n" entriesTxt; + mode = "0444"; + }; + }; +} diff --git a/modules/common/profiles/debug.nix b/modules/common/profiles/debug.nix index f17fd9830..5c8d50358 100644 --- a/modules/common/profiles/debug.nix +++ b/modules/common/profiles/debug.nix @@ -1,30 +1,28 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 # -{ - config, - lib, - ... -}: let +{ config, lib, ... }: +let cfg = config.ghaf.profiles.debug; in - with lib; { - options.ghaf.profiles.debug = { - enable = mkEnableOption "debug profile"; - }; +{ + options.ghaf.profiles.debug = { + enable = lib.mkEnableOption "debug profile"; + }; - config = mkIf cfg.enable { - # Enable default accounts and passwords - ghaf = { - users.accounts.enable = true; - # Enable development on target - development = { - nix-setup.enable = true; - # Enable some basic monitoring and debug tools - debug.tools.enable = true; - # Let us in. - ssh.daemon.enable = true; - }; + config = lib.mkIf cfg.enable { + # Enable default accounts and passwords + ghaf = { + users.accounts.enable = true; + # Enable development on target + development = { + nix-setup.enable = true; + # Enable some basic monitoring and debug tools + debug.tools.enable = true; + # Let us in. + ssh.daemon.enable = true; + usb-serial.enable = true; }; }; - } + }; +} diff --git a/modules/common/profiles/default.nix b/modules/common/profiles/default.nix index dd8f95441..9dfc34788 100644 --- a/modules/common/profiles/default.nix +++ b/modules/common/profiles/default.nix @@ -4,5 +4,6 @@ imports = [ ./debug.nix ./release.nix + ./host-hardening.nix ]; } diff --git a/modules/common/profiles/host-hardening.nix b/modules/common/profiles/host-hardening.nix new file mode 100644 index 000000000..b02308b2f --- /dev/null +++ b/modules/common/profiles/host-hardening.nix @@ -0,0 +1,25 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ config, lib, ... }: +let + cfg = config.ghaf.profiles.host-hardening; + has_host = builtins.hasAttr "host" config.ghaf; + has_secureBoot = builtins.hasAttr "secureboot" config.ghaf.host; +in +{ + options.ghaf.profiles.host-hardening = { + enable = lib.mkEnableOption "Host hardening profile"; + }; + + config = lib.mkIf cfg.enable { + ghaf = + { } + // lib.optionalAttrs (has_host && has_secureBoot) { + host = { + # Enable secure boot in the host configuration + secureboot.enable = true; + }; + }; + }; +} diff --git a/modules/common/profiles/kernel-hardening.nix b/modules/common/profiles/kernel-hardening.nix new file mode 100644 index 000000000..f4c4bac06 --- /dev/null +++ b/modules/common/profiles/kernel-hardening.nix @@ -0,0 +1,37 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ config, lib, ... }: +let + cfg = config.ghaf.profiles.hardening; +in +{ + options.ghaf.profiles.kernel-hardening = { + enable = lib.mkEnableOption "hardened profile"; + }; + + config = lib.mkIf cfg.enable { + ghaf = { + host = { + # Kernel hardening + kernel.hardening = { + enable = true; + usb.enable = true; + debug.enable = true; + virtualization.enable = true; + networking.enable = true; + inputdevices.enable = true; + hypervisor.enable = true; + }; + }; + + guest = { + # Kernel hardening + kernel.hardening = { + enable = true; + graphics.enable = true; + }; + }; + }; + }; +} diff --git a/modules/common/profiles/release.nix b/modules/common/profiles/release.nix index 190720bd4..f443cb7da 100644 --- a/modules/common/profiles/release.nix +++ b/modules/common/profiles/release.nix @@ -1,25 +1,22 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 # -{ - config, - lib, - ... -}: let +{ config, lib, ... }: +let cfg = config.ghaf.profiles.release; + inherit (lib) mkEnableOption mkIf; in - with lib; { - options.ghaf.profiles.release = { - enable = mkEnableOption "release profile"; - }; - - options.ghaf.time.timeZone = "Europe/Helsinki"; +{ + options.ghaf.profiles.release = { + enable = mkEnableOption "release profile"; + }; - config = mkIf cfg.enable { - # Enable default accounts and passwords - # TODO this needs to be refined when we define a policy for the - # processes and the UID/groups that should be enabled by default - # if not already covered by systemd - ghaf.users.accounts.enable = true; - }; - } + options.ghaf.time.timeZone = "Europe/Helsinki"; + config = mkIf cfg.enable { + # Enable default accounts and passwords + # TODO this needs to be refined when we define a policy for the + # processes and the UID/groups that should be enabled by default + # if not already covered by systemd + ghaf.users.accounts.enable = true; + }; +} diff --git a/modules/common/security/default.nix b/modules/common/security/default.nix new file mode 100644 index 000000000..e3c4b07a0 --- /dev/null +++ b/modules/common/security/default.nix @@ -0,0 +1,3 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ imports = [ ./sshkeys.nix ]; } diff --git a/targets/lenovo-x1/sshkeys.nix b/modules/common/security/sshkeys.nix similarity index 93% rename from targets/lenovo-x1/sshkeys.nix rename to modules/common/security/sshkeys.nix index 55a36eb94..12abe80e0 100644 --- a/targets/lenovo-x1/sshkeys.nix +++ b/modules/common/security/sshkeys.nix @@ -1,7 +1,11 @@ # Copyright 2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{lib, ...}: { - options.ghaf.security.sshKeys = with lib; { +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.ghaf.security.sshKeys = { getAuthKeysFileName = mkOption { type = types.str; default = "get-auth-keys"; diff --git a/modules/common/services/audio.nix b/modules/common/services/audio.nix new file mode 100644 index 000000000..fa7a88401 --- /dev/null +++ b/modules/common/services/audio.nix @@ -0,0 +1,104 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + config, + pkgs, + lib, + ... +}: +let + cfg = config.ghaf.services.audio; + inherit (lib) + mkIf + mkEnableOption + mkOption + types + ; +in +{ + options.ghaf.services.audio = { + enable = mkEnableOption "Enable audio service for audio VM"; + pulseaudioTcpPort = mkOption { + type = types.int; + default = 4713; + description = "TCP port used by Pipewire-pulseaudio service"; + }; + }; + + config = mkIf cfg.enable { + # Enable pipewire service for audioVM with pulseaudio support + security.rtkit.enable = true; + hardware.firmware = [ pkgs.sof-firmware ]; + services.pipewire = { + enable = true; + pulse.enable = true; + systemWide = true; + extraConfig = { + pipewire."10-remote-simple" = { + "context.modules" = [ + { + name = "libpipewire-module-protocol-pulse"; + args = { + # Enable TCP socket for VMs pulseaudio clients + "server.address" = [ + { + address = "tcp:4713"; + "client.access" = "unrestricted"; + } + ]; + "pulse.min.req" = "128/48000"; # 2.7ms + "pulse.default.req" = "960/48000"; # 20 milliseconds + "pulse.min.frag" = "128/48000"; # 2.7ms + "pulse.default.frag" = "512/48000"; # ~10 ms + "pulse.default.tlength" = "512/48000"; # ~10 ms + "pulse.min.quantum" = "128/48000"; # 2.7ms + }; + } + ]; + }; + }; + }; + + hardware.pulseaudio.extraConfig = '' + # Set sink and source default max volume to about 75% (0-65536) + set-sink-volume @DEFAULT_SINK@ 48000 + set-source-volume @DEFAULT_SOURCE@ 48000 + ''; + + # Allow ghaf user to access pulseaudio and pipewire + users.extraUsers.ghaf.extraGroups = [ + "audio" + "video" + "pulse-access" + "pipewire" + ]; + + # Dummy service to get pipewire and pulseaudio services started at boot + # Normally Pipewire and pulseaudio are started when they are needed by user, + # We don't have users in audiovm so we need to give PW/PA a slight kick.. + # This calls pulseaudios pa-info binary to get information about pulseaudio current + # state which starts pipewire-pulseaudio service in the process. + systemd.services.pulseaudio-starter = { + after = [ + "pipewire.service" + "network-online.target" + ]; + requires = [ + "pipewire.service" + "network-online.target" + ]; + wantedBy = [ "default.target" ]; + path = [ pkgs.coreutils ]; + enable = true; + serviceConfig = { + User = "ghaf"; + Group = "ghaf"; + }; + script = ''${pkgs.pulseaudio}/bin/pa-info > /dev/null 2>&1''; + }; + + # Open TCP port for the PDF XDG socket + networking.firewall.allowedTCPPorts = [ cfg.pulseaudioTcpPort ]; + }; +} diff --git a/modules/common/services/default.nix b/modules/common/services/default.nix new file mode 100644 index 000000000..73795c0ac --- /dev/null +++ b/modules/common/services/default.nix @@ -0,0 +1,14 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + imports = [ + ./fprint.nix + ./audio.nix + ./wifi.nix + ./firmware.nix + ./desktop.nix + ./pdfopen.nix + ./namespaces.nix + ./yubikey.nix + ]; +} diff --git a/modules/common/services/desktop.nix b/modules/common/services/desktop.nix new file mode 100644 index 000000000..30f953659 --- /dev/null +++ b/modules/common/services/desktop.nix @@ -0,0 +1,149 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: +let + inherit (builtins) hasAttr replaceStrings; + inherit (lib) + mkIf + mkEnableOption + optionals + optionalAttrs + optionalString + ; + + cfg = config.ghaf.services.desktop; + + winConfig = + if (hasAttr "reference" config.ghaf) then + if (hasAttr "programs" config.ghaf.reference) then + config.ghaf.reference.programs.windows-launcher + else + { } + else + { }; +in +# TODO: The desktop configuration needs to be re-worked. +# TODO it needs to be moved out of common and the launchers have to be set bu the reference programs NOT here +{ + options.ghaf.services.desktop = { + enable = mkEnableOption "Enable the desktop configuration"; + }; + + config = mkIf (cfg.enable && config.ghaf.givc.enable) { + ghaf = optionalAttrs (hasAttr "graphics" config.ghaf) { + profiles.graphics.compositor = "labwc"; + graphics = { + launchers = + let + cliArgs = replaceStrings [ "\n" ] [ " " ] '' + --name ${config.ghaf.givc.adminConfig.name} + --addr ${config.ghaf.givc.adminConfig.addr} + --port ${config.ghaf.givc.adminConfig.port} + ${optionalString config.ghaf.givc.enableTls "--cacert /run/givc/ca-cert.pem"} + ${optionalString config.ghaf.givc.enableTls "--cert /run/givc/gui-vm-cert.pem"} + ${optionalString config.ghaf.givc.enableTls "--key /run/givc/gui-vm-key.pem"} + ${optionalString (!config.ghaf.givc.enableTls) "--notls"} + ''; + in + [ + { + # The SPKI fingerprint is calculated like this: + # $ openssl x509 -noout -in mitmproxy-ca-cert.pem -pubkey | openssl asn1parse -noout -inform pem -out public.key + # $ openssl dgst -sha256 -binary public.key | openssl enc -base64 + name = "Chromium"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} start chromium"; + icon = "${pkgs.icon-pack}/chromium.svg"; + } + + { + name = "Trusted Browser"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} start --vm business-vm chromium"; + icon = "${pkgs.icon-pack}/thorium-browser.svg"; + } + # TODO must enable the waypipe to support more than one app in a VM + { + name = "VPN"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} start --vm business-vm gpclient"; + icon = "${pkgs.icon-pack}/yast-vpn.svg"; + } + + { + name = "Microsoft Outlook"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} start --vm business-vm outlook"; + icon = "${pkgs.icon-pack}/ms-outlook.svg"; + } + { + name = "Microsoft 365"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} start --vm business-vm office"; + icon = "${pkgs.icon-pack}/microsoft-365.svg"; + } + { + name = "Teams"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} start --vm business-vm teams"; + icon = "${pkgs.icon-pack}/teams-for-linux.svg"; + } + + { + name = "GALA"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} start gala"; + icon = "${pkgs.icon-pack}/distributor-logo-android.svg"; + } + + { + name = "PDF Viewer"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} start zathura"; + icon = "${pkgs.icon-pack}/document-viewer.svg"; + } + + { + name = "Element"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} start --vm comms-vm element"; + icon = "${pkgs.icon-pack}/element-desktop.svg"; + } + + { + name = "Slack"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} start --vm comms-vm slack"; + icon = "${pkgs.icon-pack}/slack.svg"; + } + + { + name = "AppFlowy"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} start appflowy"; + icon = "${pkgs.appflowy}/opt/data/flutter_assets/assets/images/flowy_logo.svg"; + } + + { + name = "Network Settings"; + path = "${pkgs.nm-launcher}/bin/nm-launcher"; + icon = "${pkgs.icon-pack}/preferences-system-network.svg"; + } + + { + name = "Shutdown"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} poweroff"; + icon = "${pkgs.icon-pack}/system-shutdown.svg"; + } + + { + name = "Reboot"; + path = "${pkgs.givc-cli}/bin/givc-cli ${cliArgs} reboot"; + icon = "${pkgs.icon-pack}/system-reboot.svg"; + } + ] + ++ optionals config.ghaf.reference.programs.windows-launcher.enable [ + { + name = "Windows"; + path = "${pkgs.virt-viewer}/bin/remote-viewer -f spice://${winConfig.spice-host}:${toString winConfig.spice-port}"; + icon = "${pkgs.icon-pack}/distributor-logo-windows.svg"; + } + ]; + }; + }; + }; +} diff --git a/modules/common/services/firmware.nix b/modules/common/services/firmware.nix new file mode 100644 index 000000000..1bfa9d8f5 --- /dev/null +++ b/modules/common/services/firmware.nix @@ -0,0 +1,18 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.services.firmware; + inherit (lib) mkIf mkEnableOption; +in +{ + options.ghaf.services.firmware = { + enable = mkEnableOption "PLaceholder for firmware handling"; + }; + config = mkIf cfg.enable { + hardware = { + enableRedistributableFirmware = true; + enableAllFirmware = true; + }; + }; +} diff --git a/modules/common/services/fprint.nix b/modules/common/services/fprint.nix new file mode 100644 index 000000000..8c136b548 --- /dev/null +++ b/modules/common/services/fprint.nix @@ -0,0 +1,68 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) mkEnableOption mkIf; + cfg = config.ghaf.services.fprint; +in +{ + options.ghaf.services.fprint = { + enable = mkEnableOption "Enable fingerprint reader support"; + }; + + config = mkIf cfg.enable { + # Enable service and package for fingerprint reader + services.fprintd.enable = true; + environment.systemPackages = [ pkgs.fprintd ]; + + # Enable polkit and add rules + ghaf.systemd.withPolkit = true; + security = { + polkit = { + enable = true; + debug = true; + # Polkit rules for fingerprint reader + extraConfig = '' + // Allow user to verify fingerprints + polkit.addRule(function(action, subject) { + if (action.id == "net.reactivated.fprint.device.verify" && + subject.user == "ghaf") { + return polkit.Result.YES; + } + }); + // Allow user to enroll fingerprints + polkit.addRule(function(action, subject) { + if (action.id == "net.reactivated.fprint.device.enroll" && + subject.user == "ghaf") { + return polkit.Result.YES; + } + }); + ''; + }; + # PAM rules for swaylock fingerprint reader + pam.services = { + swaylock.text = '' + # Account management. + account required pam_unix.so + + # Authentication management. + auth sufficient pam_unix.so likeauth try_first_pass + auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so + auth required pam_deny.so + + # Password management. + password sufficient pam_unix.so nullok sha512 + + # Session management. + session required pam_env.so conffile=/etc/pam/environment readenv=0 + session required pam_unix.so + ''; + }; + }; + }; +} diff --git a/modules/common/services/namespaces.nix b/modules/common/services/namespaces.nix new file mode 100644 index 000000000..c654fdf9f --- /dev/null +++ b/modules/common/services/namespaces.nix @@ -0,0 +1,21 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + inherit (builtins) attrNames hasAttr; + inherit (lib) mkOption types optionalAttrs; +in +{ + options.ghaf.namespaces = { + vms = mkOption { + type = types.listOf types.str; + default = [ ]; + description = "List of VMs currently enabled."; + }; + }; + config = { + ghaf = optionalAttrs (hasAttr "microvm" config) { + namespaces = optionalAttrs (hasAttr "vms" config.microvm) { vms = attrNames config.microvm.vms; }; + }; + }; +} diff --git a/modules/common/services/pdfopen.nix b/modules/common/services/pdfopen.nix new file mode 100644 index 000000000..6012db915 --- /dev/null +++ b/modules/common/services/pdfopen.nix @@ -0,0 +1,65 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: +let + inherit (builtins) toString; + inherit (lib) + mkEnableOption + mkOption + mkIf + types + ; + cfg = config.ghaf.services.pdfopener; + + # TODO: Fix the path to get the sshKeyPath so that + # openPdf can be exported as a normal package from + # packaged/flake-module.nix and hence easily imported + # into all targets + openPdf = pkgs.callPackage ../../../packages/openPdf { + inherit (config.ghaf.security.sshKeys) sshKeyPath; + }; +in +{ + options.ghaf.services.pdfopener = { + enable = mkEnableOption "Enable the pdf opening service"; + xdgPdfPort = mkOption { + type = types.int; + default = 1200; + description = "TCP port for the PDF XDG socket"; + }; + }; + + config = mkIf cfg.enable { + # PDF XDG handler service receives a PDF file path from the chromium-vm and executes the openpdf script + systemd.user = { + sockets."pdf" = { + unitConfig = { + Description = "PDF socket"; + }; + socketConfig = { + ListenStream = "${toString cfg.xdgPdfPort}"; + Accept = "yes"; + }; + wantedBy = [ "sockets.target" ]; + }; + + services."pdf@" = { + description = "PDF opener"; + serviceConfig = { + ExecStart = "${openPdf}/bin/openPdf"; + StandardInput = "socket"; + StandardOutput = "journal"; + StandardError = "journal"; + }; + }; + }; + + # Open TCP port for the PDF XDG socket. + networking.firewall.allowedTCPPorts = [ cfg.xdgPdfPort ]; + }; +} diff --git a/modules/common/services/wifi.nix b/modules/common/services/wifi.nix new file mode 100644 index 000000000..5685b929b --- /dev/null +++ b/modules/common/services/wifi.nix @@ -0,0 +1,54 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.ghaf.services.wifi; + inherit (lib) mkIf mkForce mkEnableOption; +in +{ + options.ghaf.services.wifi = { + enable = mkEnableOption "Wifi configuration for the net-vm"; + }; + config = mkIf cfg.enable { + networking = { + # wireless is disabled because we use NetworkManager for wireless + wireless.enable = mkForce false; + networkmanager = { + enable = true; + unmanaged = [ "ethint0" ]; + }; + }; + + environment = { + # noXlibs=false; needed for NetworkManager stuff + noXlibs = false; + + etc."NetworkManager/system-connections/Wifi-1.nmconnection" = { + text = '' + [connection] + id=Wifi-1 + uuid=33679db6-4cde-11ee-be56-0242ac120002 + type=wifi + [wifi] + mode=infrastructure + ssid=SSID_OF_NETWORK + [wifi-security] + key-mgmt=wpa-psk + psk=WPA_PASSWORD + [ipv4] + method=auto + [ipv6] + method=disabled + [proxy] + ''; + mode = "0600"; + }; + systemPackages = mkIf config.ghaf.profiles.debug.enable [ pkgs.tcpdump ]; + }; + }; +} diff --git a/modules/common/services/yubikey.nix b/modules/common/services/yubikey.nix new file mode 100644 index 000000000..1a240ab85 --- /dev/null +++ b/modules/common/services/yubikey.nix @@ -0,0 +1,60 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) + mkEnableOption + mkIf + mkOption + types + concatStrings + ; + cfg = config.ghaf.services.yubikey; + u2f_file = pkgs.writeText "u2f_mapping" config.ghaf.services.yubikey.u2fKeys; +in +{ + options.ghaf.services.yubikey = { + enable = mkEnableOption "Enable yubikey support which provide 2FA"; + + u2fKeys = mkOption { + type = types.str; + default = [ ]; + example = concatStrings [ + ## Key should in following format :,,,:,,,:... + "ghaf:SZ2CwN7EAE4Ujfxhm+CediUaT9ngoaMOqsKRDrOC+wUkTriKlc1cVtsxkOSav2r9ztaNKn/OwoHiN3BmsBYdZA==,oIdGgoGmkVrVis1kdzpvX3kXrOmBe2noFrpHqh4VKlq/WxrFk+Du670BL7DzLas+GxIPNjgdDCHo9daVzthIwQ==,es256,+presence" + ":9CEdjOg0YGpvNeisK5OW1hjjg0nRvJDBpr7X8Q4QPtxJP4iC5C6dShTxEpxmLAkqAi8x/jKCDwpt146AYAXfFg==,q8ddSEI2tIyRwB2MhRlrGZRv6ZDkEC2RYn/n33fdmK1KjBkcMy6ELUMQQDVGtsvsiQFbRS3v4qxjsgXF5BVD0A==,es256,+presence+pin" + ]; + description = "It will contain U2F Keys / public keys reterived from Yubikey hardware"; + }; + }; + + config = mkIf cfg.enable { + # Enable service and package for Yubikey + services.pcscd.enable = true; + environment.systemPackages = [ pkgs.pam_u2f ]; + + security.pam.services = { + sudo.u2fAuth = true; + gtklock.u2fAuth = true; + }; + + security.pam.u2f = { + settings = { + authfile = "${u2f_file}"; + cue = true; + }; + control = "sufficient"; + }; + + # Below rules are needed for screen locker (gtklock) to work + services.udev.extraRules = '' + KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0407", TAG+="uaccess", GROUP="kvm", MODE="0666" + ACTION=="remove", ENV{ID_BUS}=="usb", ENV{ID_VENDOR_ID}=="1050", ENV{ID_MODEL_ID}=="0407", RUN+="${pkgs.systemd}/bin/loginctl lock-sessions" + ''; + }; +} diff --git a/modules/common/systemd/base.nix b/modules/common/systemd/base.nix index 22ad7dbe4..219134909 100644 --- a/modules/common/systemd/base.nix +++ b/modules/common/systemd/base.nix @@ -5,13 +5,23 @@ lib, pkgs, ... -}: let +}: +let # Ghaf systemd config cfg = config.ghaf.systemd; + inherit (lib) + mkEnableOption + mkOption + mkIf + mkForce + types + ; + # Override minimal systemd package configuration package = - (pkgs.systemdMinimal.override { + (pkgs.systemdMinimal.override ( + { pname = cfg.withName; withAcl = true; withAnalyze = cfg.withDebug; @@ -31,7 +41,7 @@ withLibseccomp = true; inherit (cfg) withLocaled; inherit (cfg) withLogind; - withMachined = cfg.withMachines; + withMachined = cfg.withMachines || cfg.withNss; # Required for NSS in nixos inherit (cfg) withNetworkd; inherit (cfg) withNss; withOomd = true; @@ -44,17 +54,15 @@ inherit (cfg) withTimesyncd; inherit (cfg) withTpm2Tss; withUtmp = cfg.withJournal || cfg.withAudit; - } # To be removed, current systemd version 254.6 < 255 - // lib.optionalAttrs (lib.hasAttr "withVmspawn" (lib.functionArgs pkgs.systemd.override)) { + } + // lib.optionalAttrs (lib.strings.versionAtLeast pkgs.systemdMinimal.version "255.0") { withVmspawn = cfg.withMachines; - }) - .overrideAttrs (prevAttrs: { - patches = - prevAttrs.patches - ++ [ - ./systemd-boot-double-dtb-buffer-size.patch - ]; - }); + withQrencode = true; # Required for systemd-bsod (currently hardcoded in nixos) + } + )).overrideAttrs + (prevAttrs: { + patches = prevAttrs.patches ++ [ ./systemd-boot-double-dtb-buffer-size.patch ]; + }); # Definition of suppressed system units in systemd configuration. This removes the units and has priority. # Required to avoid build failures compared to only disabling units for some options. Note that errors will be silently ignored. @@ -109,9 +117,7 @@ "auditd.service" "systemd-journald-audit.socket" ]) - ++ (lib.optionals ((!cfg.withDebug) && (!cfg.withMachines)) [ - "systemd-coredump.socket" - ]) + ++ (lib.optionals ((!cfg.withDebug) && (!cfg.withMachines)) [ "systemd-coredump.socket" ]) ++ (lib.optionals (!cfg.withLogind) [ "systemd-logind.service" "dbus-org.freedesktop.login1.service" @@ -125,12 +131,8 @@ "nss-lookup.target.requires" "nss-user-lookup.target.requires" ]) - ++ (lib.optionals (!cfg.withTimesyncd) [ - "systemd-timesyncd.service" - ]) - ++ (lib.optionals (!cfg.withResolved) [ - "systemd-resolved.service" - ]) + ++ (lib.optionals (!cfg.withTimesyncd) [ "systemd-timesyncd.service" ]) + ++ (lib.optionals (!cfg.withResolved) [ "systemd-resolved.service" ]) ++ (lib.optionals (!cfg.withNetworkd) [ "network.target" "network-pre.target" @@ -170,140 +172,144 @@ "prepare-kexec.target" ]); in - with lib; { - options.ghaf.systemd = { - enable = mkEnableOption "Enable minimal systemd configuration."; +{ + options.ghaf.systemd = { + enable = mkEnableOption "Enable minimal systemd configuration."; - withName = mkOption { - description = "Set systemd name."; - type = types.str; - default = "base-systemd"; - }; + withName = mkOption { + description = "Set systemd name."; + type = types.str; + default = "base-systemd"; + }; - withLogind = mkOption { - description = "Enable systemd login daemon."; - type = types.bool; - default = true; - }; + withLogind = mkOption { + description = "Enable systemd login daemon."; + type = types.bool; + default = true; + }; - withJournal = mkOption { - description = "Enable systemd journal daemon."; - type = types.bool; - default = true; - }; + withJournal = mkOption { + description = "Enable systemd journal daemon."; + type = types.bool; + default = true; + }; - withNetworkd = mkOption { - description = "Enable systemd networking daemon."; - type = types.bool; - default = true; - }; + withNetworkd = mkOption { + description = "Enable systemd networking daemon."; + type = types.bool; + default = true; + }; - withTimesyncd = mkOption { - description = "Enable systemd timesync daemon."; - type = types.bool; - default = false; - }; + withTimesyncd = mkOption { + description = "Enable systemd timesync daemon."; + type = types.bool; + default = false; + }; - withResolved = mkOption { - description = "Enable systemd resolve daemon."; - type = types.bool; - default = false; - }; + withResolved = mkOption { + description = "Enable systemd resolve daemon."; + type = types.bool; + default = false; + }; - withRepart = mkOption { - description = "Enable systemd repart functionality."; - type = types.bool; - default = false; - }; + withRepart = mkOption { + description = "Enable systemd repart functionality."; + type = types.bool; + default = false; + }; - withHostnamed = mkOption { - description = "Enable systemd hostname daemon."; - type = types.bool; - default = false; - }; + withHostnamed = mkOption { + description = "Enable systemd hostname daemon."; + type = types.bool; + default = false; + }; - withNss = mkOption { - description = "Enable systemd Name Service Switch (NSS) functionality."; - type = types.bool; - default = false; - }; + withNss = mkOption { + description = "Enable systemd Name Service Switch (NSS) functionality."; + type = types.bool; + default = false; + }; - withEfi = mkOption { - description = "Enable systemd EFI+bootloader functionality."; - type = types.bool; - default = pkgs.stdenv.hostPlatform.isEfi; - }; + withEfi = mkOption { + description = "Enable systemd EFI+bootloader functionality."; + type = types.bool; + default = pkgs.stdenv.hostPlatform.isEfi; + }; - withApparmor = mkOption { - description = "Enable systemd apparmor functionality."; - type = types.bool; - default = false; - }; + withApparmor = mkOption { + description = "Enable systemd apparmor functionality."; + type = types.bool; + default = false; + }; - withMachines = mkOption { - description = "Enable systemd container and VM functionality."; - type = types.bool; - default = false; - }; + withMachines = mkOption { + description = "Enable systemd container and VM functionality."; + type = types.bool; + default = false; + }; - withAudit = mkOption { - description = "Enable systemd audit functionality."; - type = types.bool; - default = false; - }; + withAudit = mkOption { + description = "Enable systemd audit functionality."; + type = types.bool; + default = false; + }; - withCryptsetup = mkOption { - description = "Enable systemd LUKS2 functionality."; - type = types.bool; - default = false; - }; + withCryptsetup = mkOption { + description = "Enable systemd LUKS2 functionality."; + type = types.bool; + default = false; + }; - withFido2 = mkOption { - description = "Enable systemd Fido2 token functionality."; - type = types.bool; - default = false; - }; + withFido2 = mkOption { + description = "Enable systemd Fido2 token functionality."; + type = types.bool; + default = false; + }; - withTpm2Tss = mkOption { - description = "Enable systemd TPM functionality."; - type = types.bool; - default = false; - }; + withTpm2Tss = mkOption { + description = "Enable systemd TPM functionality."; + type = types.bool; + default = false; + }; - withPolkit = mkOption { - description = "Enable systemd polkit functionality."; - type = types.bool; - default = false; - }; + withPolkit = mkOption { + description = "Enable systemd polkit functionality."; + type = types.bool; + default = false; + }; - withSerial = mkOption { - description = "Enable systemd serial console."; - type = types.bool; - default = false; - }; + withSerial = mkOption { + description = "Enable systemd serial console."; + type = types.bool; + default = false; + }; - withLocaled = mkOption { - description = "Enable systemd locale daemon."; - type = types.bool; - default = false; - }; + withLocaled = mkOption { + description = "Enable systemd locale daemon."; + type = types.bool; + default = false; + }; - withDebug = mkOption { - description = "Enable systemd debug functionality."; - type = types.bool; - default = false; - }; + withDebug = mkOption { + description = "Enable systemd debug functionality."; + type = types.bool; + default = false; }; + }; + + config = mkIf cfg.enable { + security.auditd.enable = cfg.withAudit; + systemd = { + # Package and unit configuration + inherit package; + inherit suppressedSystemUnits; - config = mkIf cfg.enable { - systemd = { - # Package and unit configuration - inherit package; - inherit suppressedSystemUnits; + # Misc. configurations + enableEmergencyMode = cfg.withDebug; + coredump.enable = cfg.withDebug || cfg.withMachines; - # Misc. configurations - enableEmergencyMode = cfg.withDebug; - coredump.enable = cfg.withDebug || cfg.withMachines; - }; + # Service startup optimization + services.systemd-networkd-wait-online.enable = mkForce false; }; - } + }; +} diff --git a/modules/common/systemd/boot.nix b/modules/common/systemd/boot.nix index 78aea7800..a73ca3b13 100644 --- a/modules/common/systemd/boot.nix +++ b/modules/common/systemd/boot.nix @@ -5,27 +5,33 @@ lib, pkgs, ... -}: let +}: +let # Ghaf configuration flags cfg = config.ghaf.systemd.boot; cfgBase = config.ghaf.systemd; + inherit (lib) mkEnableOption mkIf optionals; + # Package configuration - package = pkgs.systemdMinimal.override { - pname = "stage1-systemd"; - inherit (cfgBase) withAudit; - inherit (cfgBase) withCryptsetup; - inherit (cfgBase) withEfi; - inherit (cfgBase) withFido2; - inherit (cfgBase) withRepart; - inherit (cfgBase) withTpm2Tss; - }; + package = pkgs.systemdMinimal.override ( + { + pname = "stage1-systemd"; + inherit (cfgBase) withAudit; + inherit (cfgBase) withCryptsetup; + inherit (cfgBase) withEfi; + inherit (cfgBase) withFido2; + inherit (cfgBase) withRepart; + inherit (cfgBase) withTpm2Tss; + } + // lib.optionalAttrs (lib.strings.versionAtLeast pkgs.systemdMinimal.version "255.0") { + withQrencode = true; # Required for systemd-bsod, which is currently hardcoded in nixos + } + ); # Suppressed initrd systemd units suppressedUnits = - [ - "multi-user.target" - ] + [ "multi-user.target" ] ++ (lib.optionals ((!cfgBase.withDebug) && (!cfgBase.withJournal)) [ "systemd-journald.service" "systemd-journald.socket" @@ -44,23 +50,26 @@ "rpcbind.target" ]); in - with lib; { - options.ghaf.systemd.boot = { - enable = mkEnableOption "Enable systemd in stage 1 of the boot (initrd)."; - }; +{ + options.ghaf.systemd.boot = { + enable = mkEnableOption "Enable systemd in stage 1 of the boot (initrd)."; + }; - config = mkIf cfg.enable { - boot.initrd = { - verbose = config.ghaf.profiles.debug.enable; - services.lvm.enable = true; - systemd = { - enable = true; - inherit package; - inherit suppressedUnits; - emergencyAccess = config.ghaf.profiles.debug.enable; - enableTpm2 = cfgBase.withTpm2Tss; - initrdBin = optionals config.ghaf.profiles.debug.enable [pkgs.lvm2 pkgs.util-linux]; - }; + config = mkIf cfg.enable { + boot.initrd = { + verbose = config.ghaf.profiles.debug.enable; + services.lvm.enable = true; + systemd = { + enable = true; + inherit package; + inherit suppressedUnits; + emergencyAccess = config.ghaf.profiles.debug.enable; + enableTpm2 = cfgBase.withTpm2Tss; + initrdBin = optionals config.ghaf.profiles.debug.enable [ + pkgs.lvm2 + pkgs.util-linux + ]; }; }; - } + }; +} diff --git a/modules/common/systemd/default.nix b/modules/common/systemd/default.nix index fc9d3b06c..6b14912e0 100644 --- a/modules/common/systemd/default.nix +++ b/modules/common/systemd/default.nix @@ -4,6 +4,6 @@ imports = [ ./base.nix ./boot.nix - # TODO hardened configs + ./harden.nix ]; } diff --git a/modules/common/systemd/harden.nix b/modules/common/systemd/harden.nix new file mode 100644 index 000000000..083864b8a --- /dev/null +++ b/modules/common/systemd/harden.nix @@ -0,0 +1,63 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + # Ghaf systemd config + cfg = config.ghaf.systemd; + apply-service-configs = configs-dir: { + services = lib.foldl' ( + services: s: + let + svc = builtins.replaceStrings [ ".nix" ] [ "" ] s; + in + services + // lib.optionalAttrs (!builtins.elem "${svc}.service" cfg.excludedHardenedConfigs) { + ${svc}.serviceConfig = import "${configs-dir}/${svc}.nix"; + } + ) { } (builtins.attrNames (builtins.readDir configs-dir)); + }; +in +{ + options.ghaf.systemd = { + withHardenedConfigs = lib.mkOption { + description = "Enable common hardened configs."; + type = lib.types.bool; + default = false; + }; + + excludedHardenedConfigs = lib.mkOption { + default = [ ]; + type = lib.types.listOf lib.types.str; + example = [ "sshd.service" ]; + description = '' + A list of units to skip when applying hardened systemd service configurations. + The main purpose of this is to provide a mechanism to exclude specific hardened + configurations for fast debugging and problem resolution. + ''; + }; + + logLevel = lib.mkOption { + description = '' + Log Level for systemd services. + Available options: "emerg", "alert", "crit", "err", "warning", "info", "debug" + ''; + type = lib.types.str; + default = "info"; + }; + }; + + config = { + systemd = lib.mkMerge [ + # Apply hardened systemd service configurations + (lib.mkIf cfg.withHardenedConfigs (apply-service-configs ./hardened-configs/common)) + + # Apply release only service configurations + (lib.mkIf ( + !cfg.withDebug && cfg.withHardenedConfigs + ) (apply-service-configs ./hardened-configs/release)) + + # Set systemd log level + { services."_global_".environment.SYSTEMD_LOG_LEVEL = cfg.logLevel; } + ]; + }; +} diff --git a/modules/common/systemd/hardened-configs/common/NetworkManager-dispatcher.nix b/modules/common/systemd/hardened-configs/common/NetworkManager-dispatcher.nix new file mode 100644 index 000000000..69c9b835a --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/NetworkManager-dispatcher.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + "AF_PACKET" + "AF_NETLINK" + "AF_UNIX" + "AF_INET" + "AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + # ProtectSystem="full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + # NoNewPrivileges=true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + # "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/dbus.nix b/modules/common/systemd/hardened-configs/common/dbus.nix new file mode 100644 index 000000000..a0de4971a --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/dbus.nix @@ -0,0 +1,181 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + IPAccounting = true; + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + "AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + ProtectHome = true; + ProtectSystem = "full"; + # ProtectProc="noaccess"; + # ReadWritePaths=[ "/etc"]; + ReadOnlyPaths = [ "/" ]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + PrivateDevices = true; + DeviceAllow = [ + "/dev/null rw" + "/dev/urandom r" + ]; + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + # Delegate=false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + LimitMEMLOCK = 0; + + ################ + # Capabilities # + ################ + + AmbientCapabilities = [ + "CAP_BPF" + "CAP_PERFMON" + ]; + CapabilityBoundingSet = [ + "CAP_SETGID" + "CAP_SETUID" + "CAP_SETPCAP" + "CAP_SYS_RESOURCE" + "CAP_AUDIT_WRITE" + # "~CAP_SYS_PACCT" + # "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + # "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + # "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + # "~CAP_SYS_MODULE" + # "~CAP_SYS_TTY_CONFIG" + # "~CAP_SYS_BOOT" + # "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + # "~CAP_LEASE" + # "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + # "~CAP_SYS_RAWIO" + # "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "@system-service" + "~@chown" + "@clock" + "@cpu-emulation" + "@debug" + "@module" + "@mount" + "@obsolete" + "@raw-io" + "@reboot" + "@resources" + "@swap" + "memfd_create" + "mincore" + "mlock" + "mlockall" + "personality" + # "~@clock" + # "~@cpu-emulation" + # "~@debug" + # "~@module" + # "~@mount" + # "~@obsolete" + # "~@privileged" + # "~@raw-io" + # "~@reboot" + # "~@resources" + # "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/dnsmasq.nix b/modules/common/systemd/hardened-configs/common/dnsmasq.nix new file mode 100644 index 000000000..ec96f2718 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/dnsmasq.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + "AF_PACKET" + "AF_NETLINK" + "AF_UNIX" + "AF_INET" + "AF_INET6" + ]; + + ############### + # File system # + ############### + + ProtectHome = true; + ProtectSystem = true; + ProtectProc = "invisible"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + PrivateDevices = true; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + # Delegate=false; + # KeyringMode="private"; + NoNewPrivileges = true; + # UMask=077; + ProtectHostname = true; + ProtectClock = true; + # ProtectControlGroups=true; + # RestrictNamespaces=true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + # "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/enable-ksm.nix b/modules/common/systemd/hardened-configs/common/enable-ksm.nix new file mode 100644 index 000000000..2f097646e --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/enable-ksm.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + "~AF_NETLINK" + #"~AF_UNIX" + "~AF_INET" + "~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + # ProtectSystem="full"; + # ProtectProc="noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + # ProtectKernelTunables=true; + # ProtectKernelModules=true; + # ProtectKernelLogs=true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + # UMask=077; + ProtectHostname = true; + ProtectClock = true; + # ProtectControlGroups=true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + # RestrictSUIDSGID=true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + # "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + # "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + # "~@clock" + "~@cpu-emulation" + "~@debug" + # "~@module" + # "~@mount" + "~@obsolete" + "~@privileged" + "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/firewall.nix b/modules/common/systemd/hardened-configs/common/firewall.nix new file mode 100644 index 000000000..56bffddbe --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/firewall.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + PrivateDevices = true; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/generate-shutdown-ramfs.nix b/modules/common/systemd/hardened-configs/common/generate-shutdown-ramfs.nix new file mode 100644 index 000000000..e29e9fece --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/generate-shutdown-ramfs.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + "~AF_NETLINK" + "~AF_UNIX" + "~AF_INET" + "~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + # ProtectSystem="full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/ghaf-session.nix b/modules/common/systemd/hardened-configs/common/ghaf-session.nix new file mode 100644 index 000000000..e8246a625 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/ghaf-session.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + ProcSubset = "pid"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + # "~@cpu-emulation" + "~@debug" + "~@module" + # "~@mount" + "~@obsolete" + "~@privileged" + # "~@raw-io" + # "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/install-microvm-netvm.nix b/modules/common/systemd/hardened-configs/common/install-microvm-netvm.nix new file mode 100644 index 000000000..d6880a9c9 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/install-microvm-netvm.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + # "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/kmod-static-nodes.nix b/modules/common/systemd/hardened-configs/common/kmod-static-nodes.nix new file mode 100644 index 000000000..dc5bf3997 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/kmod-static-nodes.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + # "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/logrotate-checkconf.nix b/modules/common/systemd/hardened-configs/common/logrotate-checkconf.nix new file mode 100644 index 000000000..b7d1399a4 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/logrotate-checkconf.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + "~@raw-io" + "~@reboot" + # "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/logrotate.nix b/modules/common/systemd/hardened-configs/common/logrotate.nix new file mode 100644 index 000000000..b7d1399a4 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/logrotate.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + "~@raw-io" + "~@reboot" + # "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/microvm-tap-interfaces@.nix b/modules/common/systemd/hardened-configs/common/microvm-tap-interfaces@.nix new file mode 100644 index 000000000..36420e297 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/microvm-tap-interfaces@.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + ProtectHome = true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/microvm-virtiofsd@.nix b/modules/common/systemd/hardened-configs/common/microvm-virtiofsd@.nix new file mode 100644 index 000000000..38f638906 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/microvm-virtiofsd@.nix @@ -0,0 +1,152 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressAllow = "localhost"; + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # TODO: change back to true when microvm catches up. + PrivateTmp = "yes"; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + # RestrictNamespaces = true; + RestrictNamespaces = [ + #"~user" + #"~pid" + #"~net" + "~uts" + #"~mnt" + #"~cgroup" + "~ipc" + ]; + + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_*" + # "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + # "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + # "~@mount" + "~@obsolete" + # "~@privileged" + "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/microvm@.nix b/modules/common/systemd/hardened-configs/common/microvm@.nix new file mode 100644 index 000000000..47715ae7f --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/microvm@.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + # ProtectKernelTunables=true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + # "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + "~@reboot" + # "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/network-local-commands.nix b/modules/common/systemd/hardened-configs/common/network-local-commands.nix new file mode 100644 index 000000000..69e19684e --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/network-local-commands.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + # ProtectSystem="full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/nscd.nix b/modules/common/systemd/hardened-configs/common/nscd.nix new file mode 100644 index 000000000..bd5c5b61d --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/nscd.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + # ProtectSystem="full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + # "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/pulseaudio.nix b/modules/common/systemd/hardened-configs/common/pulseaudio.nix new file mode 100644 index 000000000..3a3b5c97a --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/pulseaudio.nix @@ -0,0 +1,154 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + ProtectHome = true; + ProtectSystem = "strict"; + ProtectProc = "invisible"; + ReadWritePaths = [ + "/var/run/" + "/var/lib/" + ]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + # Delegate=false; + # KeyringMode="private"; + NoNewPrivileges = true; + # UMask=077; + ProtectHostname = true; + ProtectClock = true; + # ProtectControlGroups=true; + # RestrictNamespaces=true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + # "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + # "~@privileged" + "~@raw-io" + "~@reboot" + # "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/rtkit-daemon.nix b/modules/common/systemd/hardened-configs/common/rtkit-daemon.nix new file mode 100644 index 000000000..7f152cd20 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/rtkit-daemon.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + ProtectHome = true; + ProtectSystem = "strict"; + ProtectProc = "invisible"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + # Delegate=false; + # KeyringMode="private"; + NoNewPrivileges = true; + # UMask=077; + ProtectHostname = true; + ProtectClock = true; + # ProtectControlGroups=true; + # RestrictNamespaces=true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + # RestrictRealtime=true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + # "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + # "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + # "~@mount" + "~@obsolete" + # "~@privileged" + "~@raw-io" + "~@reboot" + # "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/seatd.nix b/modules/common/systemd/hardened-configs/common/seatd.nix new file mode 100644 index 000000000..8134aef04 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/seatd.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + # "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + # "~@privileged" + "~@raw-io" + "~@reboot" + # "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-fsck-root.nix b/modules/common/systemd/hardened-configs/common/systemd-fsck-root.nix new file mode 100644 index 000000000..efda36ea6 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-fsck-root.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + "~AF_NETLINK" + "~AF_UNIX" + "~AF_INET" + "~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + PrivateDevices = true; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + # "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-journal-catalog-update.nix b/modules/common/systemd/hardened-configs/common/systemd-journal-catalog-update.nix new file mode 100644 index 000000000..700f57cda --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-journal-catalog-update.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + "~CAP_SYS_TIME" + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-journal-flush.nix b/modules/common/systemd/hardened-configs/common/systemd-journal-flush.nix new file mode 100644 index 000000000..4174778d4 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-journal-flush.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + "~CAP_SYS_TIME" + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + # "~@privileged" + "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-networkd-wait-online.nix b/modules/common/systemd/hardened-configs/common/systemd-networkd-wait-online.nix new file mode 100644 index 000000000..c4450d066 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-networkd-wait-online.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + # "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-random-seed.nix b/modules/common/systemd/hardened-configs/common/systemd-random-seed.nix new file mode 100644 index 000000000..4bfd967a3 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-random-seed.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + "~AF_NETLINK" + "~AF_UNIX" + "~AF_INET" + "~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + PrivateDevices = true; + DeviceAllow = [ "/dev/null rw" ]; + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-remount-fs.nix b/modules/common/systemd/hardened-configs/common/systemd-remount-fs.nix new file mode 100644 index 000000000..d6b241892 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-remount-fs.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + "~AF_NETLINK" + "~AF_UNIX" + "~AF_INET" + "~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices = true; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + # "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-rfkill.nix b/modules/common/systemd/hardened-configs/common/systemd-rfkill.nix new file mode 100644 index 000000000..471c0af31 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-rfkill.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + PrivateDevices = true; + DeviceAllow = [ "/dev/null rw" ]; + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-tmpfiles-clean.nix b/modules/common/systemd/hardened-configs/common/systemd-tmpfiles-clean.nix new file mode 100644 index 000000000..0aec328d1 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-tmpfiles-clean.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + "~AF_NETLINK" + "~AF_UNIX" + "~AF_INET" + "~AF_INET6" + ]; + + ############### + # File system # + ############### + + ProtectHome = true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + # ProtectKernelLogs=true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + # RestrictSUIDSGID=true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-tmpfiles-setup-dev.nix b/modules/common/systemd/hardened-configs/common/systemd-tmpfiles-setup-dev.nix new file mode 100644 index 000000000..d6880a9c9 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-tmpfiles-setup-dev.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + # "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-tmpfiles-setup.nix b/modules/common/systemd/hardened-configs/common/systemd-tmpfiles-setup.nix new file mode 100644 index 000000000..cf6d109f4 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-tmpfiles-setup.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + "~AF_NETLINK" + "~AF_UNIX" + "~AF_INET" + "~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + # ProtectSystem="full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + # ProtectKernelLogs=true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + # RestrictSUIDSGID=true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + # "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-udev-trigger.nix b/modules/common/systemd/hardened-configs/common/systemd-udev-trigger.nix new file mode 100644 index 000000000..33b1c52b1 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-udev-trigger.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + "~AF_NETLINK" + "~AF_UNIX" + "~AF_INET" + "~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + PrivateDevices = true; + DeviceAllow = [ "/dev/null rw" ]; + + ########## + # Kernel # + ########## + + # ProtectKernelTunables=true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-udevd.nix b/modules/common/systemd/hardened-configs/common/systemd-udevd.nix new file mode 100644 index 000000000..35a5837f4 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-udevd.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + # ProtectSystem="full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + # ProtectKernelTunables=true; + # ProtectKernelModules=true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + # ProtectHostname=true; + ProtectClock = true; + # ProtectControlGroups=true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + # LockPersonality=true; + # MemoryDenyWriteExecute=true; + # RestrictRealtime=true; + # RestrictSUIDSGID=true; + # RemoveIPC=true + # SystemCallArchitectures="native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + # "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + # "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + # "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + # "~CAP_SYS_MODULE" + # "~CAP_SYS_TTY_CONFIG" + # "~CAP_SYS_BOOT" + # "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + # "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + # "~@clock" + # "~@cpu-emulation" + # "~@debug" + # "~@module" + # "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + # "~@reboot" + # "~@resources" + # "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/systemd-user-sessions.nix b/modules/common/systemd/hardened-configs/common/systemd-user-sessions.nix new file mode 100644 index 000000000..e29e9fece --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/systemd-user-sessions.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + "~AF_NETLINK" + "~AF_UNIX" + "~AF_INET" + "~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + # ProtectSystem="full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/tpm2-abrmd.nix b/modules/common/systemd/hardened-configs/common/tpm2-abrmd.nix new file mode 100644 index 000000000..7f152cd20 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/tpm2-abrmd.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + ProtectHome = true; + ProtectSystem = "strict"; + ProtectProc = "invisible"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + # Delegate=false; + # KeyringMode="private"; + NoNewPrivileges = true; + # UMask=077; + ProtectHostname = true; + ProtectClock = true; + # ProtectControlGroups=true; + # RestrictNamespaces=true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + # RestrictRealtime=true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + # "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + # "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + # "~@mount" + "~@obsolete" + # "~@privileged" + "~@raw-io" + "~@reboot" + # "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/user-runtime-dir@.nix b/modules/common/systemd/hardened-configs/common/user-runtime-dir@.nix new file mode 100644 index 000000000..d14ee7e79 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/user-runtime-dir@.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + "~AF_NETLINK" + #"~AF_UNIX" + "~AF_INET" + "~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + # ProtectSystem="full"; + # ProtectProc="noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + # ProtectKernelTunables=true; + # ProtectKernelModules=true; + # ProtectKernelLogs=true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + # UMask=077; + ProtectHostname = true; + ProtectClock = true; + # ProtectControlGroups=true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + # RestrictSUIDSGID=true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + # "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + # "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + # "~@clock" + "~@cpu-emulation" + "~@debug" + # "~@module" + # "~@mount" + "~@obsolete" + "~@privileged" + "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/vsockproxy.nix b/modules/common/systemd/hardened-configs/common/vsockproxy.nix new file mode 100644 index 000000000..7f152cd20 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/vsockproxy.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + ProtectHome = true; + ProtectSystem = "strict"; + ProtectProc = "invisible"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + # Delegate=false; + # KeyringMode="private"; + NoNewPrivileges = true; + # UMask=077; + ProtectHostname = true; + ProtectClock = true; + # ProtectControlGroups=true; + # RestrictNamespaces=true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + # RestrictRealtime=true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + # "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + # "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + # "~@mount" + "~@obsolete" + # "~@privileged" + "~@raw-io" + "~@reboot" + # "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/common/wpa_supplicant.nix b/modules/common/systemd/hardened-configs/common/wpa_supplicant.nix new file mode 100644 index 000000000..7938b9d20 --- /dev/null +++ b/modules/common/systemd/hardened-configs/common/wpa_supplicant.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + "AF_PACKET" + "AF_NETLINK" + "AF_UNIX" + "AF_INET" + "AF_INET6" + ]; + + ############### + # File system # + ############### + + ProtectHome = true; + ProtectSystem = "strict"; + ProtectProc = "invisible"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + PrivateDevices = true; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + # Delegate=false; + # KeyringMode="private"; + NoNewPrivileges = true; + # UMask=077; + ProtectHostname = true; + ProtectClock = true; + # ProtectControlGroups=true; + # RestrictNamespaces=true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + # "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/release/NetworkManager.nix b/modules/common/systemd/hardened-configs/release/NetworkManager.nix new file mode 100644 index 000000000..8512d9a51 --- /dev/null +++ b/modules/common/systemd/hardened-configs/release/NetworkManager.nix @@ -0,0 +1,153 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + "AF_PACKET" + "AF_NETLINK" + "AF_UNIX" + "AF_INET" + "AF_INET6" + ]; + + ############### + # File system # + ############### + + ProtectHome = true; + ProtectSystem = "strict"; + ProtectProc = "invisible"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + PrivateDevices = true; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + # Delegate=false; + # KeyringMode="private"; + NoNewPrivileges = true; + # UMask=077; + ProtectHostname = true; + ProtectClock = true; + # ProtectControlGroups=true; + # RestrictNamespaces=true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + # "~CAP_SYS_PACCT" + # "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + # "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + # "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + # "~CAP_SYS_MODULE" + # "~CAP_SYS_TTY_CONFIG" + # "~CAP_SYS_BOOT" + "CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + # "~CAP_LEASE" + # "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + "CAP_SETUID" + "CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + # "~CAP_SYS_RAWIO" + # "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "@system-service" + "@privileged" + # "~@clock" + # "~@cpu-emulation" + # "~@debug" + # "~@module" + # "~@mount" + # "~@obsolete" + # "~@privileged" + # "~@raw-io" + # "~@reboot" + # "~@resources" + # "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/release/audit.nix b/modules/common/systemd/hardened-configs/release/audit.nix new file mode 100644 index 000000000..5d7051b13 --- /dev/null +++ b/modules/common/systemd/hardened-configs/release/audit.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ######## + # Networking # + ############## + + PrivateNetwork = true; + # IPAccounting=yes + IPAddressDeny = "any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + ProtectHome = true; + ProtectSystem = "full"; + ProtectProc = "noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################ + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers= service runs as root + # DynamicUser= service runs as root + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/exampledevice + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + Delegate = false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC= service runs as root + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + "~CAP_NET_BIND_SERVICE" + "~CAP_NET_BROADCAST" + "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + # "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + # "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/release/sshd.nix b/modules/common/systemd/hardened-configs/release/sshd.nix new file mode 100644 index 000000000..58b850821 --- /dev/null +++ b/modules/common/systemd/hardened-configs/release/sshd.nix @@ -0,0 +1,161 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes; + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + "~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + # ProtectSystem="full"; + ProtectProc = "invisible"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + DeviceAllow = [ + "/dev/video0" + "/dev/video1" + "/dev/video2" + "/dev/video3" + "/dev/media0" + "/dev/media1" + ]; + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + # Delegate=false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + ProtectControlGroups = true; + RestrictNamespaces = [ + "~cgroup" + "~uts" + ]; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + LockPersonality = true; + # MemoryDenyWriteExecute=true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + # "~CAP_SYS_BOOT" + # "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + # "~@mount" + "~@obsolete" + # "~@privileged" + "~@raw-io" + # "~@reboot" + # "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/release/user@.nix b/modules/common/systemd/hardened-configs/release/user@.nix new file mode 100644 index 000000000..47d72c29c --- /dev/null +++ b/modules/common/systemd/hardened-configs/release/user@.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + # ProtectSystem="full"; + # ProtectProc="noaccess"; + # ReadWritePaths=[ "/etc"]; + PrivateTmp = true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + PrivateUsers = true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + + ######## + # Misc # + ######## + + # Delegate=false; + # KeyringMode="private"; + NoNewPrivileges = true; + UMask = 77; + ProtectHostname = true; + ProtectClock = true; + # ProtectControlGroups=true; + RestrictNamespaces = true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + # LockPersonality=true; + # MemoryDenyWriteExecute=true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + # RemoveIPC=true + SystemCallArchitectures = "native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + "~CAP_SYS_PACCT" + # "~CAP_KILL" + "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + # "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + # "~CAP_SYS_MODULE" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + # "~CAP_SYS_RAWIO" + "~CAP_SYS_PTRACE" + "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + "~@clock" + # "~@cpu-emulation" + "~@debug" + "~@module" + # "~@mount" + "~@obsolete" + # "~@privileged" + # "~@raw-io" + # "~@reboot" + # "~@resources" + "~@swap" + ]; +} diff --git a/modules/common/systemd/hardened-configs/template.nix b/modules/common/systemd/hardened-configs/template.nix new file mode 100644 index 000000000..88e304ae5 --- /dev/null +++ b/modules/common/systemd/hardened-configs/template.nix @@ -0,0 +1,151 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + ############## + # Networking # + ############## + + # PrivateNetwork=true; + # IPAccounting=yes + # IPAddressDeny="any"; + RestrictAddressFamilies = [ + #"~AF_PACKET" + #"~AF_NETLINK" + #"~AF_UNIX" + #"~AF_INET" + #"~AF_INET6" + ]; + + ############### + # File system # + ############### + + # ProtectHome=true; + # ProtectSystem="full"; + # ProtectProc="noaccess"; + # ReadWritePaths=[ "/etc"]; + # PrivateTmp=true; + + # Not applicable for the service runs as root + # PrivateMounts=true; + # ProcSubset="all"; + + ################### + # User separation # + ################### + + # Not applicable for the service runs as root + # PrivateUsers=true; + # DynamicUser=true; + + ########### + # Devices # + ########### + + # PrivateDevices=false; + # DeviceAllow=/dev/null + + ########## + # Kernel # + ########## + + # ProtectKernelTunables=true; + # ProtectKernelModules=true; + # ProtectKernelLogs=true; + + ######## + # Misc # + ######## + + # Delegate=false; + # KeyringMode="private"; + # NoNewPrivileges=true; + # UMask=077; + # ProtectHostname=true; + # ProtectClock=true; + # ProtectControlGroups=true; + # RestrictNamespaces=true; + /* + RestrictNamespaces=[ + #"~user" + #"~pid" + #"~net" + #"~uts" + #"~mnt" + #"~cgroup" + #"~ipc" + ]; + */ + # LockPersonality=true; + # MemoryDenyWriteExecute=true; + # RestrictRealtime=true; + # RestrictSUIDSGID=true; + # RemoveIPC=true + # SystemCallArchitectures="native"; + # NotifyAccess=false; + + ################ + # Capabilities # + ################ + + #AmbientCapabilities= + CapabilityBoundingSet = [ + # "~CAP_SYS_PACCT" + # "~CAP_KILL" + # "~CAP_WAKE_ALARM" + # "~CAP_DAC_* + # "~CAP_FOWNER" + # "~CAP_IPC_OWNER" + # "~CAP_BPF" + # "~CAP_LINUX_IMMUTABLE" + # "~CAP_IPC_LOCK" + # "~CAP_SYS_MODULE" + # "~CAP_SYS_TTY_CONFIG" + # "~CAP_SYS_BOOT" + # "~CAP_SYS_CHROOT" + # "~CAP_BLOCK_SUSPEND" + # "~CAP_LEASE" + # "~CAP_MKNOD" + # "~CAP_CHOWN" + # "~CAP_FSETID" + # "~CAP_SETFCAP" + # "~CAP_SETUID" + # "~CAP_SETGID" + # "~CAP_SETPCAP" + # "~CAP_MAC_ADMIN" + # "~CAP_MAC_OVERRIDE" + # "~CAP_SYS_RAWIO" + # "~CAP_SYS_PTRACE" + # "~CAP_SYS_NICE" + # "~CAP_SYS_RESOURCE" + # "~CAP_NET_ADMIN" + # "~CAP_NET_BIND_SERVICE" + # "~CAP_NET_BROADCAST" + # "~CAP_NET_RAW" + # "~CAP_AUDIT_CONTROL" + # "~CAP_AUDIT_READ" + # "~CAP_AUDIT_WRITE" + # "~CAP_SYS_ADMIN" + # "~CAP_SYSLOG" + # "~CAP_SYS_TIME + ]; + + ################ + # System calls # + ################ + + SystemCallFilter = [ + # "~@clock" + # "~@cpu-emulation" + # "~@debug" + # "~@module" + # "~@mount" + # "~@obsolete" + # "~@privileged" + # "~@raw-io" + # "~@reboot" + # "~@resources" + # "~@swap" + ]; +} diff --git a/modules/common/tpm2/default.nix b/modules/common/tpm2/default.nix deleted file mode 100644 index feed3224c..000000000 --- a/modules/common/tpm2/default.nix +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - config, - lib, - pkgs, - ... -}: let - cfg = config.ghaf.security.tpm2; -in - with lib; { - options.ghaf.security.tpm2 = { - enable = mkEnableOption "TPM2 PKCS#11 interface"; - }; - - config = mkIf cfg.enable { - security.tpm2 = { - enable = true; - pkcs11.enable = true; - abrmd.enable = true; - }; - - environment.systemPackages = mkIf config.ghaf.profiles.debug.enable [ - pkgs.opensc - pkgs.tpm2-tools - ]; - - assertions = [ - { - assertion = pkgs.stdenv.isx86_64; - message = "TPM2 is only supported on x86_64"; - } - ]; - }; - } diff --git a/modules/common/users/accounts.nix b/modules/common/users/accounts.nix index b44293022..e3822f8d7 100644 --- a/modules/common/users/accounts.nix +++ b/modules/common/users/accounts.nix @@ -1,51 +1,56 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - config, - lib, - options, - ... -}: +{ config, lib, ... }: # account for the development time login with sudo rights let cfg = config.ghaf.users.accounts; + inherit (lib) + mkEnableOption + mkOption + optionals + mkIf + types + ; in - with lib; { - #TODO Extend this to allow definition of multiple users - options.ghaf.users.accounts = { - enable = mkEnableOption "Default account Setup"; - user = mkOption { - default = "ghaf"; - type = with types; str; - description = '' - A default user to create in the system. - ''; - }; - password = mkOption { - default = "ghaf"; - type = with types; str; - description = '' - A default password for the user. - ''; - }; +{ + #TODO Extend this to allow definition of multiple users + options.ghaf.users.accounts = { + enable = mkEnableOption "Default account Setup"; + user = mkOption { + default = "ghaf"; + type = with types; str; + description = '' + A default user to create in the system. + ''; + }; + password = mkOption { + default = "ghaf"; + type = with types; str; + description = '' + A default password for the user. + ''; }; + }; - config = mkIf cfg.enable { - users = { - mutableUsers = true; - users."${cfg.user}" = { - isNormalUser = true; - inherit (cfg) password; - #TODO add "docker" use "lib.optionals" - extraGroups = - ["wheel" "video" "networkmanager"] - ++ optionals - config.ghaf.security.tpm2.enable ["tss"]; - }; - groups."${cfg.user}" = { - name = cfg.user; - members = [cfg.user]; - }; + config = mkIf cfg.enable { + users = { + mutableUsers = true; + users."${cfg.user}" = { + isNormalUser = true; + inherit (cfg) password; + #TODO add "docker" use "lib.optionals" + extraGroups = [ + "wheel" + "video" + "networkmanager" + ] ++ optionals config.security.tpm2.enable [ "tss" ]; + }; + groups."${cfg.user}" = { + name = cfg.user; + members = [ cfg.user ]; }; }; - } + # to build ghaf as ghaf-user with caches + nix.settings.trusted-users = mkIf config.ghaf.profiles.debug.enable [ cfg.user ]; + }; +} diff --git a/modules/common/version/default.nix b/modules/common/version/default.nix index 531bd8292..04b6e254e 100644 --- a/modules/common/version/default.nix +++ b/modules/common/version/default.nix @@ -8,11 +8,13 @@ lib, config, ... -}: let +}: +let ghafVersion = pkgs.writeShellScriptBin "ghaf-version" '' echo "${config.ghaf.version}" ''; -in { +in +{ options = { ghaf.version = lib.mkOption { type = lib.types.str; @@ -23,8 +25,6 @@ in { }; }; config = { - environment.systemPackages = [ - ghafVersion - ]; + environment.systemPackages = [ ghafVersion ]; }; } diff --git a/modules/common/virtualization/docker.nix b/modules/common/virtualization/docker.nix index 44b8bf1c0..6b9fa77e2 100644 --- a/modules/common/virtualization/docker.nix +++ b/modules/common/virtualization/docker.nix @@ -1,22 +1,20 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - lib, - config, - ... -}: let +{ lib, config, ... }: +let cfg = config.ghaf.virtualization.docker.daemon; + inherit (lib) mkEnableOption mkIf; in - with lib; { - options.ghaf.virtualization.docker.daemon = { - enable = mkEnableOption "Docker Daemon"; - }; +{ + options.ghaf.virtualization.docker.daemon = { + enable = mkEnableOption "Docker Daemon"; + }; - config = mkIf cfg.enable { - virtualisation.docker.enable = true; - virtualisation.docker.rootless = { - enable = true; - setSocketVariable = true; - }; + config = mkIf cfg.enable { + virtualisation.docker.enable = true; + virtualisation.docker.rootless = { + enable = true; + setSocketVariable = true; }; - } + }; +} diff --git a/modules/desktop/default.nix b/modules/desktop/default.nix index a0b4e68e1..c6aa68ab5 100644 --- a/modules/desktop/default.nix +++ b/modules/desktop/default.nix @@ -7,6 +7,5 @@ imports = [ ./graphics ./profiles - ./windows-launcher ]; } diff --git a/modules/desktop/graphics/boot.nix b/modules/desktop/graphics/boot.nix index 40599207d..bcf46262d 100644 --- a/modules/desktop/graphics/boot.nix +++ b/modules/desktop/graphics/boot.nix @@ -3,31 +3,36 @@ { config, lib, + pkgs, ... -}: let +}: +let cfg = config.ghaf.graphics.boot; in - with lib; { - options.ghaf.graphics.boot = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Enables graphical boot with plymouth. - ''; - }; +{ + options.ghaf.graphics.boot = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Enables graphical boot with plymouth. + ''; }; + }; - config = mkIf cfg.enable { - boot = { - plymouth = { - enable = true; - logo = ../../../assets/ghaf-logo.png; - }; - # Hide boot log from user completely - kernelParams = ["quiet" "udev.log_priority=3"]; - consoleLogLevel = 0; - initrd.verbose = false; + config = lib.mkIf cfg.enable { + boot = { + plymouth = { + enable = true; + logo = "${pkgs.ghaf-artwork}/ghaf-logo.png"; }; + # Hide boot log from user completely + kernelParams = [ + "quiet" + "udev.log_priority=3" + ]; + consoleLogLevel = 0; + initrd.verbose = false; }; - } + }; +} diff --git a/modules/desktop/graphics/default.nix b/modules/desktop/graphics/default.nix index 037f40502..ee8f20ddd 100644 --- a/modules/desktop/graphics/default.nix +++ b/modules/desktop/graphics/default.nix @@ -2,14 +2,12 @@ # SPDX-License-Identifier: Apache-2.0 { imports = [ - ./weston.nix ./labwc.nix - ./weston.ini.nix + ./labwc.config.nix ./waybar.config.nix ./demo-apps.nix ./fonts.nix - ./gnome.nix - ./window-manager.nix + ./login-manager.nix ./boot.nix ]; } diff --git a/modules/desktop/graphics/demo-apps.nix b/modules/desktop/graphics/demo-apps.nix index 5c10fc072..963259bcc 100644 --- a/modules/desktop/graphics/demo-apps.nix +++ b/modules/desktop/graphics/demo-apps.nix @@ -5,74 +5,64 @@ lib, config, ... -}: let +}: +let cfg = config.ghaf.graphics.demo-apps; /* - Scaled down firefox icon - */ - firefox-icon = pkgs.runCommand "firefox-icon-24x24" {} '' - mkdir -p $out/share/icons/hicolor/24x24/apps - ${pkgs.buildPackages.imagemagick}/bin/convert \ - ${pkgs.firefox}/share/icons/hicolor/128x128/apps/firefox.png \ - -resize 24x24 \ - $out/share/icons/hicolor/24x24/apps/firefox.png - ''; - - /* - Generate launchers to be used in weston.ini - - Type: mkProgramOption :: string -> bool -> option + Generate launchers to be used in the application drawer + Type: mkProgramOption :: string -> bool -> option */ - mkProgramOption = name: default: - with lib; - mkOption { - inherit default; - type = types.bool; - description = "Include package ${name} to menu and system environment"; - }; -in { - options.ghaf.graphics.demo-apps = with lib; { + mkProgramOption = + name: default: + lib.mkOption { + inherit default; + type = lib.types.bool; + description = "Include package ${name} to menu and system environment"; + }; +in +{ + options.ghaf.graphics.demo-apps = { chromium = mkProgramOption "Chromium browser" false; firefox = mkProgramOption "Firefox browser" config.ghaf.graphics.enableDemoApplications; gala-app = mkProgramOption "Gala App" false; element-desktop = mkProgramOption "Element desktop" config.ghaf.graphics.enableDemoApplications; zathura = mkProgramOption "zathura" config.ghaf.graphics.enableDemoApplications; + appflowy = mkProgramOption "Appflowy" config.ghaf.graphics.enableDemoApplications; }; config = lib.mkIf config.ghaf.profiles.graphics.enable { ghaf.graphics.launchers = lib.optional cfg.chromium { - name = "chromium"; + name = "Chromium"; path = "${pkgs.chromium}/bin/chromium --enable-features=UseOzonePlatform --ozone-platform=wayland"; - icon = "${pkgs.chromium}/share/icons/hicolor/24x24/apps/chromium.png"; + icon = "${pkgs.icon-pack}/chromium.svg"; } ++ lib.optional cfg.firefox { - name = "firefox"; + name = "Firefox"; path = "${pkgs.firefox}/bin/firefox"; - icon = "${firefox-icon}/share/icons/hicolor/24x24/apps/firefox.png"; + icon = "${pkgs.icon-pack}/firefox.svg"; } ++ lib.optional cfg.element-desktop { - name = "element"; + name = "Element"; path = "${pkgs.element-desktop}/bin/element-desktop --enable-features=UseOzonePlatform --ozone-platform=wayland"; - icon = "${pkgs.element-desktop}/share/icons/hicolor/24x24/apps/element.png"; + icon = "${pkgs.icon-pack}/element-desktop.svg"; } ++ lib.optional cfg.gala-app { - name = "gala"; + name = "GALA"; path = "${pkgs.gala-app}/bin/gala --enable-features=UseOzonePlatform --ozone-platform=wayland"; - icon = "${pkgs.gala-app}/gala/resources/icon-24x24.png"; + icon = "${pkgs.icon-pack}/distributor-logo-android.svg"; } ++ lib.optional cfg.zathura { - name = "zathura"; + name = "PDF Viewer"; path = "${pkgs.zathura}/bin/zathura"; - icon = "${pkgs.zathura}/share/icons/hicolor/32x32/apps/org.pwmt.zathura.png"; + icon = "${pkgs.icon-pack}/document-viewer.svg"; + } + ++ lib.optional (cfg.appflowy && pkgs.stdenv.isx86_64) { + name = "AppFlowy"; + path = "${pkgs.appflowy}/bin/appflowy"; + icon = "${pkgs.appflowy}/opt/data/flutter_assets/assets/images/flowy_logo.svg"; }; - environment.systemPackages = - lib.optional cfg.chromium pkgs.chromium - ++ lib.optional cfg.element-desktop pkgs.element-desktop - ++ lib.optional cfg.firefox pkgs.firefox - ++ lib.optional cfg.gala-app pkgs.gala-app - ++ lib.optional cfg.zathura pkgs.zathura; }; } diff --git a/modules/desktop/graphics/fonts.nix b/modules/desktop/graphics/fonts.nix index 8db6814d8..fda3967d2 100644 --- a/modules/desktop/graphics/fonts.nix +++ b/modules/desktop/graphics/fonts.nix @@ -5,13 +5,12 @@ lib, config, ... -}: let - inherit (config.ghaf.graphics) weston labwc; -in { - config = lib.mkIf (weston.enable || labwc.enable) { - fonts.packages = with pkgs; [ - fira-code-nerdfont - hack-font - ]; +}: +let + inherit (config.ghaf.graphics) labwc; +in +{ + config = lib.mkIf labwc.enable { + fonts.packages = builtins.attrValues { inherit (pkgs) inter fira-code-nerdfont hack-font; }; }; } diff --git a/modules/desktop/graphics/ghaf-launcher.nix b/modules/desktop/graphics/ghaf-launcher.nix index c79b05b4c..c54996896 100644 --- a/modules/desktop/graphics/ghaf-launcher.nix +++ b/modules/desktop/graphics/ghaf-launcher.nix @@ -1,31 +1,35 @@ # Copyright 2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - writeShellScriptBin, - writeTextDir, - coreutils, - nwg-drawer, - ... -}: let - drawerCSS = writeTextDir "nwg-drawer/drawer.css" '' +{ pkgs, ... }: +let + drawerCSS = pkgs.writeTextDir "nwg-drawer/drawer.css" '' /* Example configuration from: https://github.com/nwg-piotr/nwg-drawer/blob/main/drawer.css */ window { - background-color: rgba (43, 48, 59, 0.95); - color: #eeeeee + background-color: rgba(32, 32, 32, 0.9); + color: #eeeeee; + border-radius: 7px; + border: 1px solid rgba(21, 36, 24, 0.3); + box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; } /* search entry */ entry { - background-color: rgba (0, 0, 0, 0.2) + background-color: rgba (43, 43, 43, 1); + border: 1px solid rgba(46, 46, 46, 1); + } + entry:focus { + box-shadow: none; + border: 1px solid rgba(223, 92, 55, 1); } button, image { background: none; - border: none + border: none; + box-shadow: none; } button:hover { - background-color: rgba (255, 255, 255, 0.1) + background-color: rgba (255, 255, 255, 0.06) } /* in case you wanted to give category buttons a different look */ @@ -45,11 +49,22 @@ } ''; in - writeShellScriptBin - "ghaf-launcher" - '' - export XDG_CONFIG_HOME=${drawerCSS} - export XDG_CACHE_HOME=$HOME/.cache - ${coreutils}/bin/mkdir -p $XDG_CACHE_HOME - ${nwg-drawer}/bin/nwg-drawer - '' +pkgs.writeShellApplication { + name = "ghaf-launcher"; + runtimeInputs = [ + pkgs.coreutils + pkgs.nwg-drawer + ]; + bashOptions = [ ]; + text = '' + export XDG_CONFIG_HOME="$HOME/.config" + export XDG_CACHE_HOME="$HOME/.cache" + + # Temporary workaround + mkdir -p "$XDG_CACHE_HOME" "$XDG_CONFIG_HOME" + rm -rf "$HOME/.config/nwg-drawer" + ln -s "${drawerCSS}/nwg-drawer" "$HOME/.config/" + + nwg-drawer -r -c 5 -mb 60 -ml 440 -mr 440 -mt 420 -nofs -nocats -ovl + ''; +} diff --git a/modules/desktop/graphics/gnome.nix b/modules/desktop/graphics/gnome.nix deleted file mode 100644 index 0d564aee6..000000000 --- a/modules/desktop/graphics/gnome.nix +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# GNOME Desktop support -# -{ - lib, - pkgs, - config, - ... -}: let - cfg = config.ghaf.graphics.gnome; -in { - options.ghaf.graphics.gnome = { - enable = lib.mkEnableOption "gnome"; - }; - - config = lib.mkIf cfg.enable { - hardware.opengl = { - enable = true; - driSupport = true; - }; - - environment.noXlibs = false; - - services.xserver.enable = true; - services.xserver.displayManager.gdm = { - enable = true; - wayland = true; - }; - services.xserver.desktopManager.gnome.enable = true; - - environment.gnome.excludePackages = with pkgs; [ - gnome-tour - epiphany - evolution - evolutionWithPlugins - evolution-data-server - gnome.geary - gnome.gnome-music - gnome.gnome-contacts - gnome.cheese - ]; - }; -} diff --git a/modules/desktop/graphics/labwc.config.nix b/modules/desktop/graphics/labwc.config.nix new file mode 100644 index 000000000..df7cdf00c --- /dev/null +++ b/modules/desktop/graphics/labwc.config.nix @@ -0,0 +1,296 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + pkgs, + lib, + config, + ... +}: +let + cfg = config.ghaf.graphics.labwc; + + audio-ctrl = pkgs.callPackage ../../../packages/audio-ctrl { }; + gtklockStyle = pkgs.writeText "gtklock.css" '' + window { + background: rgba(29, 29, 29, 1); + color: #eee; + } + button { + box-shadow: none; + border-radius: 5px; + border: 1px solid rgba(255, 255, 255, 0.09); + background: rgba(255, 255, 255, 0.06); + } + entry { + background-color: rgba (43, 43, 43, 1); + border: 1px solid rgba(46, 46, 46, 1); + color: #eee; + } + entry:focus { + box-shadow: none; + border: 1px solid rgba(223, 92, 55, 1); + } + ''; + lockCmd = "${pkgs.gtklock}/bin/gtklock -s ${gtklockStyle}"; + + ghaf-launcher = pkgs.callPackage ./ghaf-launcher.nix { inherit config pkgs; }; + autostart = pkgs.writeShellApplication { + name = "labwc-autostart"; + + runtimeInputs = [ + pkgs.systemd + pkgs.dbus + ]; + + text = + '' + # Import environment variables to ensure it is available to user + # services + systemctl --user import-environment WAYLAND_DISPLAY + dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP + sleep 0.3 # make sure variables are set + systemctl --user reset-failed + systemctl --user stop ghaf-session.target + systemctl --user start ghaf-session.target + '' + + cfg.extraAutostart; + }; + rcXml = '' + + + 5 + + Ghaf + yes + + Inter + 12 + normal + bold + + + Inter + 12 + normal + bold + + + + + true + + + + + + + + + ${lib.optionalString config.ghaf.profiles.debug.enable '' + + + + ''} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${ + lib.optionalString (!config.ghaf.profiles.debug.enable) '' + + '' + } + + + + ${ + lib.concatStringsSep "\n" ( + map (rule: '' + + '') cfg.frameColouring + ) + } + + + yes + + + ''; + + menuXml = '' + + +

+ + + + + + + + + + + + + + + ${lib.optionalString config.ghaf.profiles.debug.enable '' + + + + ''} + + + ''; + + makoConfig = '' + font=Inter 12 + background-color=#202020e6 + progress-color=source #3D8252e6 + border-radius=5 + border-size=0 + padding=10 + default-timeout=10000 + ''; + + environment = '' + XCURSOR_THEME=breeze_cursors + + # Wayland compatibility + MOZ_ENABLE_WAYLAND=1 + ''; + + labwc-session = pkgs.writeShellApplication { + name = "labwc-session"; + + runtimeInputs = [ + pkgs.labwc + autostart + ]; + + text = "labwc -C /etc/labwc -s labwc-autostart"; + }; +in +{ + config = lib.mkIf cfg.enable { + environment.etc = { + "labwc/rc.xml".text = rcXml; + "labwc/menu.xml".text = menuXml; + "labwc/environment".text = environment; + + "mako/config".text = makoConfig; + + "greetd/environments".text = lib.mkAfter "${labwc-session}/bin/labwc-session\n"; + }; + + services.greetd.settings = { + initial_session = lib.mkIf (cfg.autologinUser != null) { + user = "ghaf"; + command = "${labwc-session}/bin/labwc-session"; + }; + }; + + systemd.user.services.ghaf-launcher = { + enable = true; + description = "Ghaf launcher daemon"; + serviceConfig = { + Type = "simple"; + ExecStart = "${ghaf-launcher}/bin/ghaf-launcher"; + Restart = "always"; + RestartSec = "1"; + }; + partOf = [ "ghaf-session.target" ]; + wantedBy = [ "ghaf-session.target" ]; + }; + + systemd.user.services.swaybg = { + enable = true; + description = "Wallpaper daemon"; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.swaybg}/bin/swaybg -m fill -i ${cfg.wallpaper}"; + }; + partOf = [ "ghaf-session.target" ]; + wantedBy = [ "ghaf-session.target" ]; + }; + + systemd.user.services.mako = { + enable = true; + description = "Notification daemon"; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.mako}/bin/mako -c /etc/mako/config"; + }; + partOf = [ "ghaf-session.target" ]; + wantedBy = [ "ghaf-session.target" ]; + }; + + systemd.user.services.lock-event = { + enable = true; + description = "Lock Event Handler"; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.swayidle}/bin/swayidle lock \"${lockCmd}\""; + }; + partOf = [ "ghaf-session.target" ]; + wantedBy = [ "ghaf-session.target" ]; + }; + + systemd.user.services.autolock = lib.mkIf cfg.autolock.enable { + enable = true; + description = "System autolock"; + serviceConfig = { + Type = "simple"; + ExecStart = '' + ${pkgs.swayidle}/bin/swayidle -w timeout ${builtins.toString cfg.autolock.duration} \ + '${pkgs.chayang}/bin/chayang && ${lockCmd}' + ''; + }; + partOf = [ "ghaf-session.target" ]; + wantedBy = [ "ghaf-session.target" ]; + }; + + ghaf.graphics.launchers = [ + { + name = "Lock"; + path = "${lockCmd}"; + icon = "${pkgs.icon-pack}/system-lock-screen.svg"; + } + { + name = "Log Out"; + path = "${pkgs.labwc}/bin/labwc --exit"; + icon = "${pkgs.icon-pack}/system-log-out.svg"; + } + ]; + }; +} diff --git a/modules/desktop/graphics/labwc.nix b/modules/desktop/graphics/labwc.nix index cd8d087d2..0876f9606 100644 --- a/modules/desktop/graphics/labwc.nix +++ b/modules/desktop/graphics/labwc.nix @@ -5,101 +5,82 @@ pkgs, config, ... -}: let +}: +let cfg = config.ghaf.graphics.labwc; - autostart = - pkgs.writeScriptBin "labwc-autostart" '' - # Import WAYLAND_DISPLAY variable to make it available to waypipe and other systemd services - ${pkgs.systemd}/bin/systemctl --user import-environment WAYLAND_DISPLAY 2>&1 & - - # Set the wallpaper. - ${pkgs.swaybg}/bin/swaybg -m fill -i ${cfg.wallpaper} >/dev/null 2>&1 & - - # Configure output directives such as mode, position, scale and transform. - ${pkgs.kanshi}/bin/kanshi >/dev/null 2>&1 & - - # Launch the top task bar. - ${pkgs.waybar}/bin/waybar -s /etc/waybar/style.css -c /etc/waybar/config >/dev/null 2>&1 & - - # Enable notifications. - ${pkgs.mako}/bin/mako >/dev/null 2>&1 & - - ${lib.optionalString cfg.lock.enable '' - # Lock screen after 5 minutes - ${pkgs.swayidle}/bin/swayidle -w timeout 300 \ - '${pkgs.swaylock-effects}/bin/swaylock -f -c 000000 \ - --clock --indicator --indicator-radius 150 --inside-ver-color 5ac379' & - ''} - '' - + cfg.extraAutostart; - rcXml = '' - - - 10 - - - - - - ${lib.concatStringsSep "\n" (map (rule: '' - - '') - cfg.frameColouring)} - - - ''; - - menuXml = '' - - - - - - - - - - - - - - - - - - - - - ''; - launchers = pkgs.callPackage ./launchers.nix {inherit config;}; -in { +in +{ options.ghaf.graphics.labwc = { enable = lib.mkEnableOption "labwc"; - lock.enable = lib.mkEnableOption "labwc screen locking"; + autolock = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to enable screen autolocking."; + }; + duration = lib.mkOption { + type = lib.types.int; + default = 300; + description = "Timeout for screen autolock in seconds."; + }; + }; + autologinUser = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = config.ghaf.users.accounts.user; + description = '' + Username of the account that will be automatically logged in to the desktop. + If unspecified, the login manager is shown as usual. + ''; + }; wallpaper = lib.mkOption { type = lib.types.path; - default = ../../../assets/wallpaper.png; + default = "${pkgs.ghaf-artwork}/ghaf-wallpaper.png"; description = "Path to the wallpaper image"; }; frameColouring = lib.mkOption { - type = lib.types.listOf (lib.types.submodule { - options = { - identifier = lib.mkOption { - type = lib.types.str; - example = "foot"; - description = "Identifier of the application"; - }; - colour = lib.mkOption { - type = lib.types.str; - example = "#00ffff"; - description = "Colour of the window frame"; + type = lib.types.listOf ( + lib.types.submodule { + options = { + identifier = lib.mkOption { + type = lib.types.str; + example = "foot"; + description = "Identifier of the application"; + }; + colour = lib.mkOption { + type = lib.types.str; + example = "#006305"; + description = "Colour of the window frame"; + }; }; - }; - }); + } + ); default = [ { identifier = "foot"; - colour = "#00ffff"; + colour = "#006305"; + } + # TODO these should reference the VM and not the application that is + # relayed through waypipe. Ideally this would match using metadata + # through Wayland security context. + { + identifier = "dev.scpp.saca.gala"; + colour = "#027d7b"; + } + { + identifier = "chromium-browser"; + colour = "#630505"; + } + { + identifier = "org.pwmt.zathura"; + colour = "#122263"; + } + { + identifier = "Element"; + colour = "#337aff"; + } + { + identifier = "AppFlowy"; + colour = "#4c3f7a"; } ]; description = "List of applications and their frame colours"; @@ -112,63 +93,41 @@ in { }; config = lib.mkIf cfg.enable { - ghaf.graphics.window-manager-common.enable = true; + ghaf.graphics.login-manager.enable = true; - environment.systemPackages = with pkgs; - [labwc launchers] - # Below sway packages needed for screen locking - ++ lib.optionals config.ghaf.graphics.labwc.lock.enable [swaylock-effects swayidle] - # Grim screenshot tool is used for labwc debug-builds - ++ lib.optionals config.ghaf.profiles.debug.enable [grim]; + environment.systemPackages = + [ + pkgs.labwc + pkgs.ghaf-theme + pkgs.adwaita-icon-theme - # It will create /etc/pam.d/swaylock file for authentication - security.pam.services = lib.mkIf config.ghaf.graphics.labwc.lock.enable {swaylock = {};}; + (import ./launchers.nix { inherit pkgs config; }) + ] + # Grim screenshot tool is used for labwc debug-builds + ++ lib.optionals config.ghaf.profiles.debug.enable [ pkgs.grim ]; - environment.etc = { - "labwc/rc.xml".text = rcXml; - "labwc/menu.xml".text = menuXml; - "labwc/themerc".source = "${pkgs.labwc}/share/doc/labwc/themerc"; - }; + # It will create a /etc/pam.d/ file for authentication + security.pam.services.gtklock = { }; - # Next 2 services/targets are taken from official weston documentation - # and adjusted for labwc - # https://wayland.pages.freedesktop.org/weston/toc/running-weston.html - systemd.user.services."labwc" = { + systemd.user.targets.ghaf-session = { enable = true; - description = "labwc, a Wayland compositor, as a user service TEST"; - documentation = ["man:labwc(1)"]; - after = ["ghaf-session.service"]; - serviceConfig = { - # Previously there was "notify" type, but for some reason - # systemd kills labwc.service because of timeout (even if it is disabled). - # "simple" works pretty well, so let's leave it. - Type = "simple"; - #TimeoutStartSec = "60"; - #WatchdogSec = "20"; - # Defaults to journal - StandardOutput = "journal"; - StandardError = "journal"; - ExecStart = "${pkgs.labwc}/bin/labwc -C /etc/labwc -s ${autostart}/bin/labwc-autostart"; - #GPU pt needs some time to start - labwc fails to restart 3 times in avg. - ExecStartPre = "${pkgs.coreutils}/bin/sleep 3"; - Restart = "on-failure"; - RestartSec = "1"; - - # Ivan N: adding openssh into the PATH since it is needed for waypipe to work - Environment = "PATH=${pkgs.openssh}/bin:$PATH"; + description = "Ghaf labwc session"; + unitConfig = { + BindsTo = [ "graphical-session.target" ]; + After = [ "graphical-session-pre.target" ]; + Wants = [ "graphical-session-pre.target" ]; }; - environment = { - WLR_RENDERER = "pixman"; - # See: https://github.com/labwc/labwc/blob/0.6.5/docs/environment - XKB_DEFAULT_LAYOUT = "us,fi"; - XKB_DEFAULT_OPTIONS = "XKB_DEFAULT_OPTIONS=grp:alt_shift_toggle"; - XDG_CURRENT_DESKTOP = "wlroots"; - MOZ_ENABLE_WAYLAND = "1"; - XCURSOR_THEME = "breeze_cursors"; - WLR_NO_HARDWARE_CURSORS = "1"; - _JAVA_AWT_WM_NONREPARENTING = "1"; - }; - wantedBy = ["default.target"]; }; + + services.upower.enable = true; + fonts.fontconfig.defaultFonts.sansSerif = [ "Inter" ]; + + ghaf.graphics.launchers = lib.mkIf config.ghaf.profiles.debug.enable [ + { + name = "Terminal"; + path = "${pkgs.foot}/bin/foot"; + icon = "${pkgs.icon-pack}/utilities-terminal.svg"; + } + ]; }; } diff --git a/modules/desktop/graphics/launchers.nix b/modules/desktop/graphics/launchers.nix index 29133918c..dd61d8423 100644 --- a/modules/desktop/graphics/launchers.nix +++ b/modules/desktop/graphics/launchers.nix @@ -1,30 +1,27 @@ # Copyright 2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - pkgs, - config, - makeDesktopItem, - ... -}: let - toDesktop = elem: - (makeDesktopItem { +{ pkgs, config, ... }: +let + toDesktop = + elem: + (pkgs.makeDesktopItem { inherit (elem) name icon; genericName = elem.name; desktopName = elem.name; comment = "Secured Ghaf Application"; exec = elem.path; - }) - .overrideAttrs (prevAttrs: { - checkPhase = - prevAttrs.checkPhase - + '' + }).overrideAttrs + (prevAttrs: { + checkPhase = + prevAttrs.checkPhase + + '' - # Check that the icon's path exists - [[ -f "${elem.icon}" ]] || (echo "The icon's path ${elem.icon} doesn't exist" && exit 1) - ''; - }); + # Check that the icon's path exists + [[ -f "${elem.icon}" ]] || (echo "The icon's path ${elem.icon} doesn't exist" && exit 1) + ''; + }); in - pkgs.symlinkJoin { - name = "ghaf-desktop-entries"; - paths = map toDesktop config.ghaf.graphics.launchers; - } +pkgs.symlinkJoin { + name = "ghaf-desktop-entries"; + paths = map toDesktop config.ghaf.graphics.launchers; +} diff --git a/modules/desktop/graphics/login-manager.nix b/modules/desktop/graphics/login-manager.nix new file mode 100644 index 000000000..3b885ff7a --- /dev/null +++ b/modules/desktop/graphics/login-manager.nix @@ -0,0 +1,74 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + pkgs, + config, + ... +}: +let + cfg = config.ghaf.graphics.login-manager; + gtkgreetStyle = pkgs.writeText "gtkgreet.css" '' + window { + background: rgba(29, 29, 29, 1); + color: #eee; + } + button { + box-shadow: none; + border-radius: 5px; + border: 1px solid rgba(255, 255, 255, 0.09); + background: rgba(255, 255, 255, 0.06); + } + entry { + background-color: rgba (43, 43, 43, 1); + border: 1px solid rgba(46, 46, 46, 1); + color: #eee; + } + entry:focus { + box-shadow: none; + border: 1px solid rgba(223, 92, 55, 1); + } + ''; +in +{ + options.ghaf.graphics.login-manager = { + enable = lib.mkEnableOption "login manager using greetd"; + }; + + config = lib.mkIf cfg.enable { + services.greetd = { + enable = true; + settings = { + default_session = + let + greeter-autostart = pkgs.writeShellApplication { + name = "greeter-autostart"; + runtimeInputs = [ + pkgs.greetd.gtkgreet + pkgs.wayland-logout + ]; + text = '' + gtkgreet -l -s ${gtkgreetStyle} + wayland-logout + ''; + }; + in + { + command = "${pkgs.labwc}/bin/labwc -C /etc/labwc -s ${greeter-autostart}/bin/greeter-autostart"; + }; + }; + }; + + services.seatd = { + enable = true; + group = "video"; + }; + + users.users.greeter.extraGroups = [ "video" ]; + + #Allow video group to change brightness + services.udev.extraRules = '' + ACTION=="add", SUBSYSTEM=="backlight", RUN+="${pkgs.coreutils}/bin/chgrp video $sys$devpath/brightness", RUN+="${pkgs.coreutils}/bin/chmod a+w $sys$devpath/brightness" + ''; + }; +} diff --git a/modules/desktop/graphics/waybar.config.nix b/modules/desktop/graphics/waybar.config.nix index 16e7117c9..d9e9e33ad 100644 --- a/modules/desktop/graphics/waybar.config.nix +++ b/modules/desktop/graphics/waybar.config.nix @@ -5,32 +5,29 @@ lib, config, ... -}: let +}: +let cfg = config.ghaf.graphics.labwc; - networkDevice = config.ghaf.hardware.definition.network.pciDevices; + inherit (config.ghaf.hardware.definition.network) pciDevices; + inherit (import ../../../lib/icons.nix { inherit pkgs lib; }) svgToPNG; - ghaf-icon = pkgs.runCommand "ghaf-icon-24x24" {} '' - mkdir -p $out/share/icons/hicolor/24x24/apps - ${pkgs.buildPackages.imagemagick}/bin/convert \ - ${../../../assets/ghaf-logo.png} \ - -resize 24x24 \ - $out/share/icons/hicolor/24x24/apps/ghaf-icon-24x24.png - ''; + launchpad-icon = svgToPNG "launchpad" "${pkgs.ghaf-artwork}/icons/launchpad.svg" "38x38"; + admin-icon = svgToPNG "admin" "${pkgs.ghaf-artwork}/icons/admin-cog.svg" "24x24"; + ghaf-icon = svgToPNG "ghaf-white" "${pkgs.ghaf-artwork}/icons/ghaf-white.svg" "24x24"; - wifiDevice = lib.lists.findFirst (d: d.name != null) null networkDevice; - wifi-signal-strength = pkgs.callPackage ../../../packages/wifi-signal-strength {wifiDevice = wifiDevice.name;}; - ghaf-launcher = pkgs.callPackage ./ghaf-launcher.nix {inherit config pkgs;}; - timeZone = - if config.time.timeZone != null - then config.time.timeZone - else "UTC"; -in { + wifiDevice = lib.lists.findFirst (d: d.name != null) null pciDevices; + wifi-signal-strength = pkgs.callPackage ../../../packages/wifi-signal-strength { + wifiDevice = wifiDevice.name; + }; + timeZone = if config.time.timeZone != null then config.time.timeZone else "UTC"; +in +{ config = lib.mkIf cfg.enable { ghaf.graphics.launchers = [ { name = "Terminal"; path = "${pkgs.foot}/bin/foot"; - icon = "${pkgs.foot}/share/icons/hicolor/48x48/apps/foot.png"; + icon = "${pkgs.icon-pack}/utilities-terminal.svg"; } ]; environment.etc."waybar/config" = { @@ -38,11 +35,18 @@ in { # Modified from default waybar configuration file https://github.com/Alexays/Waybar/blob/master/resources/config '' { - "height": 30, // Waybar height (to be removed for auto height) + "height": 48, // Waybar height "spacing": 4, // Gaps between modules (4px) - "modules-left": ["custom/launcher"], + "modules-left": ["custom/launchpad", "custom/ghaf-settings"], "modules-center": ["sway/window"], - "modules-right": ["pulseaudio", "custom/network1", "backlight", "battery", "clock", "tray"], + "position": "bottom", + "mode": "dock", + "spacing": 4, + "margin-top": 3, + "margin-bottom": 5, + "margin-left": 200, + "margin-right": 200, + "modules-right": ["pulseaudio", "custom/network1", "battery", "custom/admin", "clock", "tray"], "keyboard-state": { "numlock": true, "capslock": true, @@ -59,9 +63,7 @@ in { "clock": { "timezone": "${timeZone}", "tooltip-format": "{:%d %b %Y}\n{calendar}", - // should be "{:%a %-d %b %-I:%M %#p}" - // see github.com/Alexays/Waybar/issues/1469 - "format": "{:%a %d %b %I:%M %p}" + "format": "{:%a %d %b %H:%M}" }, "backlight": { // "device": "acpi_video1", @@ -91,9 +93,20 @@ in { }, '' + '' - "custom/launcher": { + "custom/launchpad": { + "format": " ", + "on-click": "${pkgs.procps}/bin/pkill -USR1 nwg-drawer", + "tooltip": false + }, + "custom/ghaf-settings": { + "format": " ", + // Placeholder for the actual Ghaf settings app + "on-click": "${pkgs.libnotify}/bin/notify-send 'Ghaf Platform ${lib.strings.fileContents ../../../.version}'", + "tooltip": false + }, + "custom/admin": { "format": " ", - "on-click": "${ghaf-launcher}/bin/ghaf-launcher", + "on-click": "${pkgs.nm-launcher}/bin/nm-launcher", "tooltip": false }, "pulseaudio": { @@ -119,13 +132,14 @@ in { # Modified from default waybar style file https://github.com/Alexays/Waybar/blob/master/resources/style.css '' * { - font-family: FontAwesome, Inter, Roboto, sans-serif; - font-size: 14px; + font-family: FontAwesome, Inter, sans-serif; + font-size: 16px; + border: none; + border-radius: 5px; } window#waybar { - background-color: rgba(43, 48, 59, 0.5); - border-bottom: 3px solid rgba(100, 114, 125, 0.5); + background-color: rgba(32, 32, 32, 0.9); color: #ffffff; transition-property: background-color; transition-duration: .5s; @@ -166,24 +180,22 @@ in { } #workspaces button.focused { - background-color: #64727D; box-shadow: inset 0 -3px #ffffff; } - #workspaces button.urgent { - background-color: #eb4d4b; - } #clock, #battery, #backlight, #custom-network1, - #custom-launcher, + #custom-launchpad, + #custom-ghaf-settings, + #custom-admin, #pulseaudio, #tray, #window, #workspaces { - margin: 0 4px; + padding: 0 20px; } .modules-left > widget:first-child > #workspaces { @@ -194,112 +206,43 @@ in { margin-right: 0; } + #pulseaudio, + #custom-network1, + #backlight, + #battery, #clock { - background-color: #64727D; - padding-left: 10; - padding-right: 10; - } - - #battery { - background-color: #ffffff; - color: #000000; padding-left: 10; padding-right: 10; } - #battery.charging, #battery.plugged { - color: #ffffff; - background-color: #26A65B; - } - - @keyframes blink { - to { - background-color: #ffffff; - color: #000000; - } - } - - #battery.critical:not(.charging) { - background-color: #f53c3c; - color: #ffffff; - animation-name: blink; - animation-duration: 0.5s; - animation-timing-function: linear; - animation-iteration-count: infinite; - animation-direction: alternate; - } - label:focus { background-color: #000000; } - #backlight { - background-color: #90b1b1; - padding-left: 10; - padding-right: 10; - } - - #custom-network1 { - background-color: #2980b9; - min-width: 16px; - padding-left: 10; - padding-right: 10; - } - - #custom-network1.disconnected { - background-color: #f53c3c; - } - - #pulseaudio { - background-color: #f1c40f; - color: #000000; - padding-left: 10; - padding-right: 10; - } - - #pulseaudio.muted { - background-color: #90b1b1; - color: #2a5c45; - } - - #tray { - background-color: #2980b9; - } - #tray > .passive { -gtk-icon-effect: dim; } - #tray > .needs-attention { - -gtk-icon-effect: highlight; - background-color: #eb4d4b; - } - - #language { - background: #00b093; - color: #740864; - padding: 0 5px; - margin: 0 5px; - min-width: 16px; - } - - #keyboard-state { - background: #97e1ad; - color: #000000; - padding: 0 0px; - margin: 0 5px; - min-width: 16px; + #custom-launchpad { + font-size: 20px; + background-image: url("${launchpad-icon}"); + background-position: center; + background-repeat: no-repeat; + margin-left: 13px; } - #keyboard-state > label { - padding: 0 5px; + #custom-ghaf-settings { + font-size: 20px; + background-image: url("${ghaf-icon}"); + background-position: center; + background-repeat: no-repeat; + padding-left: 10; + padding-right: 10; } - #keyboard-state > label.locked { - background: rgba(0, 0, 0, 0.2); - } - #custom-launcher { - font-size: 20px; background-image: url("${ghaf-icon}/share/icons/hicolor/24x24/apps/ghaf-icon-24x24.png"); + #custom-admin { + font-size: 20px; + background-image: url("${admin-icon}"); background-position: center; background-repeat: no-repeat; padding-left: 10; @@ -310,5 +253,16 @@ in { # The UNIX file mode bits mode = "0644"; }; + + systemd.user.services.waybar = { + enable = true; + description = "waybar menu"; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.waybar}/bin/waybar -s /etc/waybar/style.css -c /etc/waybar/config"; + }; + partOf = [ "ghaf-session.target" ]; + wantedBy = [ "ghaf-session.target" ]; + }; }; } diff --git a/modules/desktop/graphics/weston.ini.nix b/modules/desktop/graphics/weston.ini.nix deleted file mode 100644 index 144752c92..000000000 --- a/modules/desktop/graphics/weston.ini.nix +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - pkgs, - lib, - config, - ... -}: let - cfg = config.ghaf.graphics.weston; - mkLauncher = { - # Add the name field to unify with Labwc launchers - name, - path, - icon, - }: '' - [launcher] - name=${name} - path=${path} - icon=${icon} - - ''; - - /* - Generate launchers to be used in weston.ini - - Type: mkLaunchers :: [{path, icon}] -> string - - */ - mkLaunchers = lib.concatMapStrings mkLauncher; - - defaultLauncher = [ - # Keep weston-terminal launcher always enabled explicitly since if someone adds - # a launcher on the panel, the launcher will replace weston-terminal launcher. - { - name = "terminal"; - path = "${pkgs.weston}/bin/weston-terminal"; - icon = "${pkgs.weston}/share/weston/icon_terminal.png"; - } - ]; -in { - config = lib.mkIf cfg.enable { - ghaf.graphics.launchers = defaultLauncher; - environment.etc."xdg/weston/weston.ini" = { - text = - '' - # Disable screen locking - [core] - idle-time=0 - - [shell] - locking=false - background-image=${../../../assets/wallpaper.png} - background-type=scale-crop - num-workspaces=2 - - # Set the keyboard layout for weston to US by default - [keyboard] - keymap_layout=us,fi - - # Enable Hack font for weston-terminal - [terminal] - font=Hack - font-size=16 - - '' - + mkLaunchers config.ghaf.graphics.launchers; - - # The UNIX file mode bits - mode = "0644"; - }; - }; -} diff --git a/modules/desktop/graphics/weston.nix b/modules/desktop/graphics/weston.nix deleted file mode 100644 index 85a95bab8..000000000 --- a/modules/desktop/graphics/weston.nix +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - lib, - pkgs, - config, - ... -}: let - cfg = config.ghaf.graphics.weston; - waylandSocket = "wayland-1"; -in { - options.ghaf.graphics.weston = { - enable = lib.mkEnableOption "weston"; - }; - - config = lib.mkIf cfg.enable { - ghaf.graphics.window-manager-common.enable = true; - - environment.systemPackages = with pkgs; [ - weston - ]; - - # Next 2 services/targets are taken from official weston documentation: - # https://wayland.pages.freedesktop.org/weston/toc/running-weston.html - - # Weston socket - systemd.user.sockets."weston" = { - unitConfig = { - Description = "Weston, a Wayland compositor"; - Documentation = "man:weston(1) man:weston.ini(5)"; - }; - socketConfig = { - ListenStream = "%t/${waylandSocket}"; - }; - wantedBy = ["weston.service"]; - }; - - # Weston service - systemd.user.services."weston" = { - enable = true; - description = "Weston, a Wayland compositor, as a user service TEST"; - documentation = ["man:weston(1) man:weston.ini(5)" "https://wayland.freedesktop.org/"]; - requires = ["weston.socket"]; - after = ["weston.socket" "ghaf-session.service"]; - serviceConfig = { - Type = "notify"; - #TimeoutStartSec = "60"; - #WatchdogSec = "20"; - # Defaults to journal - StandardOutput = "journal"; - StandardError = "journal"; - ExecStart = "${pkgs.weston}/bin/weston --modules=systemd-notify.so"; - #GPU pt needs some time to start - weston fails to restart 3 times in avg. - ExecStartPre = "${pkgs.coreutils}/bin/sleep 3"; - # Set WAYLAND_DISPLAY variable to make it available to waypipe and other systemd services - ExecStartPost = "${pkgs.systemd}/bin/systemctl --user set-environment WAYLAND_DISPLAY=${waylandSocket}"; - Restart = "on-failure"; - RestartSec = "1"; - # Ivan N: I do not know if this is bug or feature of NixOS, but - # when I add weston.ini file to environment.etc, the file ends up in - # /etc/xdg directory on the filesystem, while NixOS uses - # /run/current-system/sw/etc/xdg directory and goes into same directory - # searching for weston.ini even if /etc/xdg is already in XDG_CONFIG_DIRS - # The solution is to add /etc/xdg one more time for weston service. - # It does not affect on system-wide XDG_CONFIG_DIRS variable. - # - # Ivan N: adding openssh into the PATH since it is needed for waypipe to work - Environment = "XDG_CONFIG_DIRS=$XDG_CONFIG_DIRS:/etc/xdg PATH=${pkgs.openssh}/bin:$PATH"; - }; - wantedBy = ["default.target"]; - }; - }; -} diff --git a/modules/desktop/graphics/window-manager.nix b/modules/desktop/graphics/window-manager.nix deleted file mode 100644 index 3249b553f..000000000 --- a/modules/desktop/graphics/window-manager.nix +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - lib, - pkgs, - config, - ... -}: let - cfg = config.ghaf.graphics.window-manager-common; -in { - options.ghaf.graphics.window-manager-common = with lib; { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Common parts for every wlroots-based window manager/compositor. - ''; - }; - }; - - config = lib.mkIf cfg.enable { - hardware.opengl = { - enable = true; - driSupport = true; - }; - - environment.noXlibs = false; - - environment.systemPackages = with pkgs; [ - # Seatd is needed to manage log-in process for wayland sessions - seatd - ]; - - # Next services/targets are taken from official weston documentation: - # https://wayland.pages.freedesktop.org/weston/toc/running-weston.html - - systemd.user.targets."ghaf-session" = { - description = "Ghaf graphical session"; - bindsTo = ["ghaf-session.target"]; - before = ["ghaf-session.target"]; - }; - - systemd.services."ghaf-session" = { - description = "Ghaf graphical session"; - - # Make sure we are started after logins are permitted. - after = ["systemd-user-sessions.service"]; - - # if you want you can make it part of the graphical session - #Before=graphical.target - - # not necessary but just in case - #ConditionPathExists=/dev/tty7 - - serviceConfig = { - Type = "simple"; - Environment = "XDG_SESSION_TYPE=wayland"; - ExecStart = "${pkgs.systemd}/bin/systemctl --wait --user start ghaf-session.target"; - - # The user to run the session as. Pick one! - User = config.ghaf.users.accounts.user; - Group = config.ghaf.users.accounts.user; - - # Set up a full user session for the user, required by desktop environment. - PAMName = "${pkgs.shadow}/bin/login"; - - # A virtual terminal is needed. - TTYPath = "/dev/tty7"; - TTYReset = "yes"; - TTYVHangup = "yes"; - TTYVTDisallocate = "yes"; - - # Try to grab tty . - StandardInput = "tty-force"; - - # Defaults to journal, in case it doesn't adjust it accordingly - #StandardOutput=journal - StandardError = "journal"; - - # Log this user with utmp, letting it show up with commands 'w' and 'who'. - UtmpIdentifier = "tty7"; - UtmpMode = "user"; - }; - wantedBy = ["multi-user.target"]; - }; - - # systemd service for seatd - systemd.services."seatd" = { - description = "Seat management daemon"; - documentation = ["man:seatd(1)"]; - serviceConfig = { - Type = "simple"; - ExecStart = "${pkgs.seatd}/bin/seatd -g video"; - Restart = "always"; - RestartSec = "1"; - }; - wantedBy = ["multi-user.target"]; - }; - }; -} diff --git a/modules/desktop/profiles/applications.nix b/modules/desktop/profiles/applications.nix index c891708cc..4bd6628c0 100644 --- a/modules/desktop/profiles/applications.nix +++ b/modules/desktop/profiles/applications.nix @@ -1,26 +1,22 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 # -{ - config, - lib, - ... -}: let +{ config, lib, ... }: +let cfg = config.ghaf.profiles.applications; in - with lib; { - options.ghaf.profiles.applications = { - enable = mkEnableOption "Some sample applications"; - #TODO Create options to allow enabling individual apps - #weston.ini.nix mods needed - }; +{ + options.ghaf.profiles.applications = { + enable = lib.mkEnableOption "Some sample applications"; + #TODO Create options to allow enabling individual apps + }; - config = mkIf cfg.enable { - # TODO: Needs more generic support for defining application launchers - # across different window managers. - ghaf = { - profiles.graphics.enable = true; - graphics.enableDemoApplications = true; - }; + config = lib.mkIf cfg.enable { + # TODO: Needs more generic support for defining application launchers + # across different window managers. + ghaf = { + profiles.graphics.enable = true; + graphics.enableDemoApplications = true; }; - } + }; +} diff --git a/modules/desktop/profiles/graphics.nix b/modules/desktop/profiles/graphics.nix index afbdb9929..426a908b7 100644 --- a/modules/desktop/profiles/graphics.nix +++ b/modules/desktop/profiles/graphics.nix @@ -4,52 +4,93 @@ { config, lib, + pkgs, ... -}: let +}: +let cfg = config.ghaf.profiles.graphics; - compositors = ["weston" "gnome" "labwc"]; + compositors = [ "labwc" ]; + renderers = [ + "vulkan" + "pixman" + "gles2" + ]; + ghaf-open = pkgs.callPackage ../../../packages/ghaf-open { }; + + inherit (lib) + mkEnableOption + mkOption + types + mkIf + ; in - with lib; { - options.ghaf.profiles.graphics = { - enable = mkEnableOption "Graphics profile"; - compositor = mkOption { - type = types.enum compositors; - default = "weston"; - description = '' - Which Wayland compositor to use. +{ + options.ghaf.profiles.graphics = { + enable = mkEnableOption "Graphics profile"; + compositor = mkOption { + type = types.enum compositors; + default = "labwc"; + description = '' + Which Wayland compositor to use. + + Choose one of: ${lib.concatStringsSep "," compositors} + ''; + }; + renderer = lib.mkOption { + type = lib.types.enum renderers; + default = "pixman"; + description = '' + Which wlroots renderer to use. - Choose one of: ${lib.concatStringsSep "," compositors} - ''; - }; + Choose one of: ${lib.concatStringsSep "," renderers} + ''; }; + }; - options.ghaf.graphics = with lib; { - launchers = mkOption { - description = "Labwc application launchers to show in launch bar"; - default = []; - type = with types; - listOf - (submodule { - options.name = mkOption { + options.ghaf.graphics = { + launchers = mkOption { + description = "Labwc application launchers to show in launch bar"; + default = [ ]; + type = types.listOf ( + types.submodule { + options = { + name = mkOption { description = "Name of the application"; - type = str; + type = types.str; }; - options.path = mkOption { + path = mkOption { description = "Path to the executable to be launched"; - type = path; + type = types.path; }; - options.icon = mkOption { + icon = mkOption { description = "Path of the icon"; - type = path; + type = types.path; }; - }); - }; - enableDemoApplications = mkEnableOption "some applications for demoing"; + }; + } + ); }; + enableDemoApplications = mkEnableOption "some applications for demoing"; + }; + + config = mkIf cfg.enable { + hardware.graphics.enable = true; + environment.noXlibs = false; + environment.sessionVariables = { + WLR_RENDERER = cfg.renderer; + XDG_SESSION_TYPE = "wayland"; + WLR_NO_HARDWARE_CURSORS = 1; + XKB_DEFAULT_LAYOUT = "us,ara,fi"; + XKB_DEFAULT_OPTIONS = "grp:alt_shift_toggle"; + # Set by default in labwc, but possibly not in other compositors + XDG_CURRENT_DESKTOP = "wlroots"; + _JAVA_AWT_WM_NONREPARENTING = 1; + }; + + environment.systemPackages = lib.optionals config.ghaf.profiles.debug.enable [ ghaf-open ]; - config = mkIf cfg.enable { - ghaf.graphics.weston.enable = cfg.compositor == "weston"; - ghaf.graphics.gnome.enable = cfg.compositor == "gnome"; - ghaf.graphics.labwc.enable = cfg.compositor == "labwc"; + ghaf.graphics = { + labwc.enable = cfg.compositor == "labwc"; }; - } + }; +} diff --git a/modules/desktop/windows-launcher/default.nix b/modules/desktop/windows-launcher/default.nix deleted file mode 100644 index 4f113aa5d..000000000 --- a/modules/desktop/windows-launcher/default.nix +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - lib, - pkgs, - config, - ... -}: let - cfg = config.ghaf.windows-launcher; - windows-launcher = pkgs.callPackage ../../../packages/windows-launcher {enableSpice = cfg.spice;}; -in { - options.ghaf.windows-launcher = { - enable = lib.mkEnableOption "Windows launcher"; - }; - - options.ghaf.windows-launcher.spice = lib.mkEnableOption "remote access to the virtual machine using spice"; - - options.ghaf.windows-launcher.spice-port = lib.mkOption { - description = "Spice port"; - type = lib.types.int; - default = 5900; - }; - - options.ghaf.windows-launcher.spice-host = lib.mkOption { - description = "Spice host"; - type = lib.types.str; - default = "192.168.101.2"; - }; - - config = lib.mkIf cfg.enable { - ghaf.graphics.launchers = lib.mkIf (!cfg.spice) [ - { - name = "windows"; - path = "${windows-launcher}/bin/windows-launcher-ui"; - icon = "${pkgs.gnome.adwaita-icon-theme}/share/icons/Adwaita/16x16/mimetypes/application-x-executable.png"; - } - ]; - - networking.firewall.allowedTCPPorts = lib.mkIf cfg.spice [cfg.spice-port]; - environment.systemPackages = [windows-launcher]; - }; -} diff --git a/modules/disko/disko-ab-partitions.nix b/modules/disko/disko-ab-partitions.nix new file mode 100644 index 000000000..34bfc12a9 --- /dev/null +++ b/modules/disko/disko-ab-partitions.nix @@ -0,0 +1,168 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# This partition scheme contains three common partitions and ZFS pool. +# Some partitions are duplicated for the future AB SWupdate implementation. +# +# First three partitions are related to the boot process: +# - boot : Bootloader partition +# - ESP-A : (500M) Kernel and initrd +# - ESP-B : (500M) +# +# ZFS datasets do not necessary need to have specified size and can be +# allocated dynamically. Quotas only restrict the maximum size of +# datasets, but do not reserve the space in the pool. +# The ZFS pool contains next datasets: +# - root-A : (30G) Root FS +# - root-B : (30G) +# - vm-storage-A : (30G) Possible standalone pre-built VM images are stored here +# - vm-storage-B : (30G) +# - reserved-A : (10G) Reserved dataset, no use +# - reserved-B : (10G) +# - gp-storage : (50G) General purpose storage for some common insecure cases +# - recovery : (no quota) Recovery factory image is stored here +# - storagevm: (no quota) Dataset is meant to be used for StorageVM +{ pkgs, ... }: +{ + # TODO Keep ZFS-related parts of the configuration here for now. + # This allows to have all config dependencies in one place and cleans + # other targets' configs from unnecessary components. + networking.hostId = "8425e349"; + boot = { + initrd.availableKernelModules = [ "zfs" ]; + supportedFilesystems = [ "zfs" ]; + }; + disko = { + # 8GB is the recommeneded minimum for ZFS, so we are using this for VMs to avoid `cp` oom errors. + memSize = 8192; + extraPostVM = '' + ${pkgs.zstd}/bin/zstd --compress $out/*raw + rm $out/*raw + ''; + extraRootModules = [ "zfs" ]; + devices = { + disk.disk1 = { + type = "disk"; + imageSize = "15G"; + content = { + type = "gpt"; + partitions = { + boot = { + name = "boot"; + size = "1M"; + type = "EF02"; + priority = 1; # Needs to be first partition + }; + esp_a = { + name = "ESP_A"; + size = "500M"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ + "umask=0077" + "nofail" + ]; + }; + }; + esp_b = { + name = "ESP_B"; + size = "500M"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountOptions = [ + "umask=0077" + "nofail" + ]; + }; + }; + zfs_1 = { + size = "100%"; + content = { + type = "zfs"; + pool = "zfspool"; + }; + }; + }; + }; + }; + zpool = { + zfspool = { + type = "zpool"; + rootFsOptions = { + mountpoint = "none"; + acltype = "posixacl"; + }; + datasets = { + "root_a" = { + type = "zfs_fs"; + mountpoint = "/"; + options = { + mountpoint = "/"; + quota = "30G"; + }; + }; + "vm_storage_a" = { + type = "zfs_fs"; + options = { + mountpoint = "/vm_storage"; + quota = "30G"; + }; + }; + "reserved_a" = { + type = "zfs_fs"; + options = { + mountpoint = "none"; + quota = "10G"; + }; + }; + "root_b" = { + type = "zfs_fs"; + options = { + mountpoint = "none"; + quota = "30G"; + }; + }; + "vm_storage_b" = { + type = "zfs_fs"; + options = { + mountpoint = "none"; + quota = "30G"; + }; + }; + "reserved_b" = { + type = "zfs_fs"; + options = { + mountpoint = "none"; + quota = "10G"; + }; + }; + "gp_storage" = { + type = "zfs_fs"; + options = { + mountpoint = "/gp_storage"; + quota = "50G"; + }; + }; + "recovery" = { + type = "zfs_fs"; + options = { + mountpoint = "none"; + }; + }; + "storagevm" = { + type = "zfs_fs"; + options = { + mountpoint = "/storagevm"; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/modules/disko/lenovo-x1-disko-basic.nix b/modules/disko/disko-basic-partition-v1.nix similarity index 83% rename from modules/disko/lenovo-x1-disko-basic.nix rename to modules/disko/disko-basic-partition-v1.nix index 229f7a97a..527e44929 100644 --- a/modules/disko/lenovo-x1-disko-basic.nix +++ b/modules/disko/disko-basic-partition-v1.nix @@ -3,13 +3,14 @@ # Example to create a bios compatible gpt partition # To use this example, you will need to specify a device i.e. # { disko.devices.disk1.device = "/dev/sda"; } +{ pkgs, ... }: { disko.devices = { disk.disk1 = { type = "disk"; #TODO: hardcoding the size for now until 544 is merged #https://github.com/nix-community/disko/pull/544 - imageSize = "10G"; + imageSize = "15G"; content = { type = "gpt"; partitions = { @@ -26,7 +27,7 @@ type = "filesystem"; format = "vfat"; mountpoint = "/boot"; - mountOptions = ["umask=0077"]; + mountOptions = [ "umask=0077" ]; }; }; root = { @@ -50,14 +51,18 @@ type = "filesystem"; format = "ext4"; mountpoint = "/"; - mountOptions = [ - "defaults" - ]; + mountOptions = [ "defaults" ]; }; }; }; }; }; }; - disko.memSize = 2048; + disko = { + memSize = 4096; + extraPostVM = '' + ${pkgs.zstd}/bin/zstd --compress $out/*raw + rm $out/*raw + ''; + }; } diff --git a/modules/disko/disko-basic-postboot.nix b/modules/disko/disko-basic-postboot.nix index c0b48f731..cb601b9d3 100644 --- a/modules/disko/disko-basic-postboot.nix +++ b/modules/disko/disko-basic-postboot.nix @@ -1,6 +1,7 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{pkgs, ...}: let +{ pkgs, ... }: +let postBootCmds = '' set -xeuo pipefail @@ -57,6 +58,7 @@ # Finally resize the filesystem inside the logical volume ${pkgs.e2fsprogs}/bin/resize2fs "$DEVPATH" ''; -in { +in +{ boot.postBootCommands = postBootCmds; } diff --git a/modules/disko/disko-zfs-postboot.nix b/modules/disko/disko-zfs-postboot.nix new file mode 100644 index 000000000..3c28cdac8 --- /dev/null +++ b/modules/disko/disko-zfs-postboot.nix @@ -0,0 +1,38 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ pkgs, ... }: +let + postBootCmds = '' + set -xeuo pipefail + + # Check which physical disk is used by ZFS + ZFS_POOLNAME=$(${pkgs.zfs}/bin/zpool list | ${pkgs.gnugrep}/bin/grep -v NAME | ${pkgs.gawk}/bin/awk '{print $1}') + ZFS_LOCATION=$(${pkgs.zfs}/bin/zpool status -P | ${pkgs.gnugrep}/bin/grep dev | ${pkgs.gawk}/bin/awk '{print $1}') + + # Get the actual device path + P_DEVPATH=$(readlink -f "$ZFS_LOCATION") + + # Extract the partition number using regex + if [[ "$P_DEVPATH" =~ [0-9]+$ ]]; then + PARTNUM=$(echo "$P_DEVPATH" | ${pkgs.gnugrep}/bin/grep -o '[0-9]*$') + PARENT_DISK=$(echo "$P_DEVPATH" | ${pkgs.gnused}/bin/sed 's/[0-9]*$//') + else + echo "No partition number found in device path: $P_DEVPATH" + fi + + # Fix GPT first + ${pkgs.gptfdisk}/bin/sgdisk "$PARENT_DISK" -e + + # Call partprobe to update kernel's partitions + ${pkgs.parted}/bin/partprobe + + # Extend the partition to use unallocated space + ${pkgs.parted}/bin/parted -s -a opt "$PARENT_DISK" "resizepart $PARTNUM 100%" + + # Extend ZFS pool to use newly allocated space + ${pkgs.zfs}/bin/zpool online -e "$ZFS_POOLNAME" "$ZFS_LOCATION" + ''; +in +{ + boot.postBootCommands = postBootCmds; +} diff --git a/modules/disko/flake-module.nix b/modules/disko/flake-module.nix index 34353f96a..1b94ecdf5 100644 --- a/modules/disko/flake-module.nix +++ b/modules/disko/flake-module.nix @@ -1,13 +1,18 @@ # Copyright 2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{inputs, ...}: { +{ inputs, ... }: +{ flake.nixosModules = { - # TODO: rename this module to what it actually does rather than what model it's for. - # We version the disko partitiong module so that we can update it without breaking existing systems - disko-lenovo-x1-basic-v1.imports = [ + disko-basic-partition-v1.imports = [ inputs.disko.nixosModules.disko - ./lenovo-x1-disko-basic.nix + ./disko-basic-partition-v1.nix ./disko-basic-postboot.nix ]; + + disko-ab-partitions-v1.imports = [ + inputs.disko.nixosModules.disko + ./disko-ab-partitions.nix + ./disko-zfs-postboot.nix + ]; }; } diff --git a/modules/flake-module.nix b/modules/flake-module.nix index cdbe92292..fe22ac7e8 100644 --- a/modules/flake-module.nix +++ b/modules/flake-module.nix @@ -3,20 +3,35 @@ # # Modules to be exported from Flake # -{inputs, ...}: { - imports = [./disko/flake-module.nix]; +{ inputs, ... }: +{ + imports = [ + ./disko/flake-module.nix + ./hardware/flake-module.nix + ./microvm/flake-module.nix + ./givc/flake-module.nix + ]; flake.nixosModules = { common.imports = [ ./common - {ghaf.development.nix-setup.nixpkgs = inputs.nixpkgs;} + { + ghaf.development.nix-setup.nixpkgs = inputs.nixpkgs; + nixpkgs.overlays = [ inputs.ghafpkgs.overlays.default ]; + } ]; - desktop.imports = [./desktop]; - host.imports = [./host]; - jetpack.imports = [./jetpack]; - jetpack-microvm.imports = [./jetpack-microvm]; - lanzaboote.imports = [./lanzaboote]; - microvm.imports = [./microvm]; - polarfire.imports = [./polarfire]; + desktop.imports = [ ./desktop ]; + host.imports = [ ./host ]; + imx8.imports = [ ./imx8 ]; + jetpack.imports = [ ./jetpack ]; + jetpack-microvm.imports = [ ./jetpack-microvm ]; + lanzaboote.imports = [ ./lanzaboote ]; + microvm.imports = [ ./microvm ]; + polarfire.imports = [ ./polarfire ]; + reference-appvms.imports = [ ./reference/appvms ]; + reference-personalize.imports = [ ./reference/personalize ]; + reference-profiles.imports = [ ./reference/profiles ]; + reference-programs.imports = [ ./reference/programs ]; + reference-services.imports = [ ./reference/services ]; }; } diff --git a/modules/givc/adminvm.nix b/modules/givc/adminvm.nix new file mode 100644 index 000000000..e501ea145 --- /dev/null +++ b/modules/givc/adminvm.nix @@ -0,0 +1,30 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.givc.adminvm; + inherit (lib) mkEnableOption mkIf; +in +{ + options.ghaf.givc.adminvm = { + enable = mkEnableOption "Enable adminvm givc module."; + }; + + config = mkIf (cfg.enable && config.ghaf.givc.enable) { + # Configure admin service + givc.admin = { + enable = true; + inherit (config.ghaf.givc.adminConfig) name; + inherit (config.ghaf.givc.adminConfig) addr; + inherit (config.ghaf.givc.adminConfig) port; + inherit (config.ghaf.givc.adminConfig) protocol; + services = [ + "givc-ghaf-host-debug.service" + "givc-net-vm.service" + "givc-gui-vm.service" + "givc-audio-vm.service" + ]; + tls.enable = config.ghaf.givc.enableTls; + }; + }; +} diff --git a/modules/givc/appvm.nix b/modules/givc/appvm.nix new file mode 100644 index 000000000..6fe698785 --- /dev/null +++ b/modules/givc/appvm.nix @@ -0,0 +1,50 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + givc, + ... +}: +let + cfg = config.ghaf.givc.appvm; + inherit (lib) + mkOption + mkEnableOption + mkIf + types + ; + vmEntry = vm: builtins.filter (x: x.name == vm) config.ghaf.networking.hosts.entries; + address = vm: lib.head (builtins.map (x: x.ip) (vmEntry vm)); +in +{ + options.ghaf.givc.appvm = { + enable = mkEnableOption "Enable appvm givc module."; + name = mkOption { + type = types.str; + default = "appvm"; + description = "Name of the appvm."; + }; + applications = mkOption { + type = types.str; + default = "{}"; + description = "Applications to run in the appvm."; + }; + }; + + config = mkIf (cfg.enable && config.ghaf.givc.enable) { + # Configure appvm service + givc.appvm = { + enable = true; + inherit (cfg) name; + inherit (cfg) applications; + addr = address cfg.name; + port = "9000"; + tls.enable = config.ghaf.givc.enableTls; + admin = config.ghaf.givc.adminConfig; + }; + + # Quick fix to allow linger (linger option in user def. currently doesn't work, e.g., bc mutable) + systemd.tmpfiles.rules = [ "f /var/lib/systemd/linger/${config.ghaf.users.accounts.user}" ]; + }; +} diff --git a/modules/givc/audiovm.nix b/modules/givc/audiovm.nix new file mode 100644 index 000000000..1ef30a751 --- /dev/null +++ b/modules/givc/audiovm.nix @@ -0,0 +1,30 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.givc.audiovm; + inherit (lib) mkEnableOption mkIf; + hostName = "audio-vm"; +in +{ + options.ghaf.givc.audiovm = { + enable = mkEnableOption "Enable audiovm givc module."; + }; + + config = mkIf (cfg.enable && config.ghaf.givc.enable) { + # Configure audiovm service + givc.sysvm = + let + audiovmEntry = builtins.filter (x: x.name == hostName) config.ghaf.networking.hosts.entries; + addr = lib.head (builtins.map (x: x.ip) audiovmEntry); + in + { + enable = true; + name = hostName; + inherit addr; + port = "9000"; + tls.enable = config.ghaf.givc.enableTls; + admin = config.ghaf.givc.adminConfig; + }; + }; +} diff --git a/modules/givc/common.nix b/modules/givc/common.nix new file mode 100644 index 000000000..6306376ec --- /dev/null +++ b/modules/givc/common.nix @@ -0,0 +1,73 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.givc; + inherit (lib) + mkOption + mkEnableOption + mkIf + types + ; + mitmEnabled = + config.ghaf.virtualization.microvm.idsvm.enable + && config.ghaf.virtualization.microvm.idsvm.mitmproxy.enable; + mitmExtraArgs = lib.optionalString mitmEnabled "--user-data-dir=/home/${config.ghaf.users.accounts.user}/.config/chromium/Default --test-type --ignore-certificate-errors-spki-list=Bq49YmAq1CG6FuBzp8nsyRXumW7Dmkp7QQ/F82azxGU="; +in +{ + options.ghaf.givc = { + enable = mkEnableOption "Enable gRPC inter-vm communication"; + enableTls = mkOption { + description = "Enable TLS for gRPC communication globally, or disable for debugging."; + type = types.bool; + default = false; + }; + idsExtraArgs = mkOption { + description = "Extra arguments for applications when IDS/MITM is enabled."; + type = types.str; + default = mitmExtraArgs; + }; + appPrefix = mkOption { + description = "Common application path prefix."; + type = types.str; + default = "/run/current-system/sw/bin"; + }; + adminConfig = mkOption { + description = "Admin server configuration."; + type = types.submodule { + options = { + name = mkOption { + description = "Host name of admin server"; + type = types.str; + }; + addr = mkOption { + description = "Address of admin server"; + type = types.str; + }; + port = mkOption { + description = "Port of admin server"; + type = types.str; + }; + protocol = mkOption { + description = "Protocol of admin server"; + type = types.str; + }; + }; + }; + }; + }; + config = mkIf cfg.enable { + # Givc admin server configuration + ghaf.givc.adminConfig = + let + adminvmEntry = builtins.filter (x: x.name == "admin-vm-debug") config.ghaf.networking.hosts.entries; + addr = lib.head (builtins.map (x: x.ip) adminvmEntry); + in + { + name = "admin-vm-debug"; + inherit addr; + port = "9001"; + protocol = "tcp"; + }; + }; +} diff --git a/modules/givc/flake-module.nix b/modules/givc/flake-module.nix new file mode 100644 index 000000000..8118eb941 --- /dev/null +++ b/modules/givc/flake-module.nix @@ -0,0 +1,46 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ inputs, ... }: +{ + flake.nixosModules = { + givc-adminvm.imports = [ + inputs.givc.nixosModules.admin + ./common.nix + ./adminvm.nix + ]; + givc-host.imports = [ + inputs.givc.nixosModules.host + ./common.nix + ./host.nix + ]; + givc-guivm.imports = [ + inputs.givc.nixosModules.sysvm + ./common.nix + ./guivm.nix + { + # Include givc overlay to import app + nixpkgs.overlays = [ inputs.givc.overlays.default ]; + } + ]; + givc-netvm.imports = [ + inputs.givc.nixosModules.sysvm + ./common.nix + ./netvm.nix + ]; + givc-audiovm.imports = [ + inputs.givc.nixosModules.sysvm + ./common.nix + ./audiovm.nix + ]; + givc-appvm.imports = [ + inputs.givc.nixosModules.appvm + ./common.nix + ./appvm.nix + ]; + givc-gpiovm.imports = [ + inputs.givc.nixosModules.sysvm + ./common.nix + ./gpiovm.nix + ]; + }; +} diff --git a/modules/givc/gpiovm.nix b/modules/givc/gpiovm.nix new file mode 100644 index 000000000..adf960fff --- /dev/null +++ b/modules/givc/gpiovm.nix @@ -0,0 +1,37 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + givc, + ... +}: +let + cfg = config.ghaf.givc.gpiovm; + inherit (lib) mkEnableOption mkIf; + hostName = "gpio-vm"; +in +{ + options.ghaf.givc.gpiovm = { + enable = mkEnableOption "Enable gpiovm givc module."; + }; + + config = mkIf (cfg.enable && config.ghaf.givc.enable) { + # Configure gpiovm service + givc.sysvm = + let + gpiovmEntry = builtins.filter (x: x.name == hostName) config.ghaf.networking.hosts.entries; + addr = lib.head (builtins.map (x: x.ip) gpiovmEntry); + in + { + enable = true; + name = hostName; + inherit addr; + port = "9000"; + wifiManager = true; + hwidService = true; + tls.enable = config.ghaf.givc.enableTls; + admin = config.ghaf.givc.adminConfig; + }; + }; +} diff --git a/modules/givc/guivm.nix b/modules/givc/guivm.nix new file mode 100644 index 000000000..4a7e8d737 --- /dev/null +++ b/modules/givc/guivm.nix @@ -0,0 +1,30 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.givc.guivm; + inherit (lib) mkEnableOption mkIf; + hostName = "gui-vm"; +in +{ + options.ghaf.givc.guivm = { + enable = mkEnableOption "Enable guivm givc module."; + }; + + config = mkIf (cfg.enable && config.ghaf.givc.enable) { + # Configure guivm service + givc.sysvm = + let + guivmEntry = builtins.filter (x: x.name == hostName) config.ghaf.networking.hosts.entries; + addr = lib.head (builtins.map (x: x.ip) guivmEntry); + in + { + enable = true; + name = hostName; + inherit addr; + port = "9000"; + tls.enable = config.ghaf.givc.enableTls; + admin = config.ghaf.givc.adminConfig; + }; + }; +} diff --git a/modules/givc/host.nix b/modules/givc/host.nix new file mode 100644 index 000000000..6849accc9 --- /dev/null +++ b/modules/givc/host.nix @@ -0,0 +1,41 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + givc, + ... +}: +let + cfg = config.ghaf.givc.host; + inherit (builtins) map filter attrNames; + inherit (lib) mkEnableOption mkIf head; + hostName = "ghaf-host-debug"; +in +{ + options.ghaf.givc.host = { + enable = mkEnableOption "Enable host givc module."; + }; + + config = mkIf (cfg.enable && config.ghaf.givc.enable) { + # Configure host service + givc.host = + let + getIp = + name: head (map (x: x.ip) (filter (x: x.name == name) config.ghaf.networking.hosts.entries)); + addr = getIp hostName; + in + { + enable = true; + name = hostName; + inherit addr; + port = "9000"; + services = [ + "reboot.target" + "poweroff.target" + ] ++ map (vmName: "microvm@${vmName}.service") (attrNames config.microvm.vms); + tls.enable = config.ghaf.givc.enableTls; + admin = config.ghaf.givc.adminConfig; + }; + }; +} diff --git a/modules/givc/netvm.nix b/modules/givc/netvm.nix new file mode 100644 index 000000000..aa22d3b0f --- /dev/null +++ b/modules/givc/netvm.nix @@ -0,0 +1,37 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + givc, + ... +}: +let + cfg = config.ghaf.givc.netvm; + inherit (lib) mkEnableOption mkIf; + hostName = "net-vm"; +in +{ + options.ghaf.givc.netvm = { + enable = mkEnableOption "Enable netvm givc module."; + }; + + config = mkIf (cfg.enable && config.ghaf.givc.enable) { + # Configure netvm service + givc.sysvm = + let + netvmEntry = builtins.filter (x: x.name == hostName) config.ghaf.networking.hosts.entries; + addr = lib.head (builtins.map (x: x.ip) netvmEntry); + in + { + enable = true; + name = hostName; + inherit addr; + port = "9000"; + wifiManager = true; + hwidService = true; + tls.enable = config.ghaf.givc.enableTls; + admin = config.ghaf.givc.adminConfig; + }; + }; +} diff --git a/modules/hardware/common/default.nix b/modules/hardware/common/default.nix new file mode 100644 index 000000000..4ba3c653b --- /dev/null +++ b/modules/hardware/common/default.nix @@ -0,0 +1,12 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + imports = [ + ./usb/internal.nix + ./usb/external.nix + ./usb/vhotplug.nix + ./devices.nix + ./kernel.nix + ./qemu.nix + ]; +} diff --git a/modules/hardware/common/devices.nix b/modules/hardware/common/devices.nix new file mode 100644 index 000000000..6e47345f8 --- /dev/null +++ b/modules/hardware/common/devices.nix @@ -0,0 +1,90 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + inherit (lib) mkOption types mkForce; +in +{ + options.ghaf.hardware.devices = { + netvmPCIPassthroughModule = mkOption { + type = types.attrsOf types.anything; + default = { }; + description = '' + PCI devices to passthrough to the netvm. + ''; + }; + guivmPCIPassthroughModule = mkOption { + type = types.attrsOf types.anything; + default = { }; + description = '' + PCI devices to passthrough to the guivm. + ''; + }; + audiovmPCIPassthroughModule = mkOption { + type = types.attrsOf types.anything; + default = { }; + description = '' + PCI devices to passthrough to the audiovm. + ''; + }; + guivmVirtioInputHostEvdevModule = mkOption { + type = types.attrsOf types.anything; + default = { }; + description = '' + Virtio evdev paths' to passthrough to the guivm. + ''; + }; + }; + + config = { + ghaf.hardware.devices = { + netvmPCIPassthroughModule = { + microvm.devices = mkForce ( + builtins.map (d: { + bus = "pci"; + inherit (d) path; + }) config.ghaf.hardware.definition.network.pciDevices + ); + ghaf.hardware.definition.network.pciDevices = config.ghaf.hardware.definition.network.pciDevices; + }; + + guivmPCIPassthroughModule = { + microvm.devices = mkForce ( + builtins.map (d: { + bus = "pci"; + inherit (d) path; + }) config.ghaf.hardware.definition.gpu.pciDevices + ); + ghaf.hardware.definition.gpu.pciDevices = config.ghaf.hardware.definition.gpu.pciDevices; + }; + + audiovmPCIPassthroughModule = { + microvm.devices = mkForce ( + builtins.map (d: { + bus = "pci"; + inherit (d) path; + }) config.ghaf.hardware.definition.audio.pciDevices + ); + ghaf.hardware.definition.audio.pciDevices = config.ghaf.hardware.definition.audio.pciDevices; + }; + + guivmVirtioInputHostEvdevModule = { + microvm.qemu.extraArgs = + builtins.concatMap + (d: [ + "-device" + "virtio-input-host-pci,evdev=${d}" + ]) + ( + config.ghaf.hardware.definition.input.keyboard.evdev + ++ config.ghaf.hardware.definition.input.mouse.evdev + ++ config.ghaf.hardware.definition.input.touchpad.evdev + ++ config.ghaf.hardware.definition.input.misc.evdev + ); + + # TODO: Remove this once wifi-signal-strength is changed + ghaf.hardware.definition.network.pciDevices = config.ghaf.hardware.definition.network.pciDevices; + }; + }; + }; +} diff --git a/modules/hardware/common/kernel.nix b/modules/hardware/common/kernel.nix new file mode 100644 index 000000000..e13202295 --- /dev/null +++ b/modules/hardware/common/kernel.nix @@ -0,0 +1,90 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# Module for Kernel Configuration Definitions +# +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) mkOption types optionalAttrs; + inherit (builtins) + concatStringsSep + filter + map + hasAttr + ; + + # Only x86 targets with hw definition supported at the moment + inherit (pkgs.stdenv.hostPlatform) isx86; + fullVirtualization = isx86 && (hasAttr "hardware" config.ghaf); +in +{ + options.ghaf.kernel = { + host = mkOption { + type = types.attrs; + default = { }; + description = "Host kernel configuration"; + }; + guivm = mkOption { + type = types.attrs; + default = { }; + description = "GuiVM kernel configuration"; + }; + audiovm = mkOption { + type = types.attrs; + default = { }; + description = "AudioVM kernel configuration"; + }; + }; + + config = { + # Host kernel configuration + boot = optionalAttrs fullVirtualization { + initrd = { + inherit (config.ghaf.hardware.definition.host.kernelConfig.stage1) kernelModules; + }; + inherit (config.ghaf.hardware.definition.host.kernelConfig.stage2) kernelModules; + kernelParams = + let + # PCI device passthroughs for vfio + filterDevices = filter (d: d.vendorId != null && d.productId != null); + mapPciIdsToString = map (d: "${d.vendorId}:${d.productId}"); + vfioPciIds = mapPciIdsToString ( + filterDevices ( + config.ghaf.hardware.definition.network.pciDevices + ++ config.ghaf.hardware.definition.gpu.pciDevices + ++ config.ghaf.hardware.definition.audio.pciDevices + ) + ); + in + config.ghaf.hardware.definition.host.kernelConfig.kernelParams + ++ [ "vfio-pci.ids=${concatStringsSep "," vfioPciIds}" ]; + }; + + # Guest kernel configurations + ghaf.kernel = optionalAttrs fullVirtualization { + guivm = { + boot = { + initrd = { + inherit (config.ghaf.hardware.definition.gpu.kernelConfig.stage1) kernelModules; + }; + inherit (config.ghaf.hardware.definition.gpu.kernelConfig.stage2) kernelModules; + inherit (config.ghaf.hardware.definition.gpu.kernelConfig) kernelParams; + }; + }; + audiovm = { + boot = { + initrd = { + inherit (config.ghaf.hardware.definition.audio.kernelConfig.stage1) kernelModules; + }; + inherit (config.ghaf.hardware.definition.audio.kernelConfig.stage2) kernelModules; + inherit (config.ghaf.hardware.definition.audio.kernelConfig) kernelParams; + }; + }; + }; + }; +} diff --git a/modules/hardware/common/qemu.nix b/modules/hardware/common/qemu.nix new file mode 100644 index 000000000..d9a02c018 --- /dev/null +++ b/modules/hardware/common/qemu.nix @@ -0,0 +1,45 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ config, lib, ... }: +let + inherit (builtins) hasAttr; + inherit (lib) + mkOption + types + optionals + optionalAttrs + ; +in +{ + options.ghaf.qemu = { + guivm = mkOption { + type = types.attrs; + default = { }; + description = "Extra qemu arguments for GuiVM"; + }; + }; + + config = { + ghaf.qemu.guivm = optionalAttrs (hasAttr "hardware" config.ghaf) { + microvm.qemu.extraArgs = + [ + # Button + "-device" + "button" + # Battery + "-device" + "battery" + # AC adapter + "-device" + "acad" + ] + ++ optionals (hasAttr "yubikey" config.ghaf.hardware.usb.external.qemuExtraArgs) config.ghaf.hardware.usb.external.qemuExtraArgs.yubikey + ++ optionals (hasAttr "fpr0" config.ghaf.hardware.usb.internal.qemuExtraArgs) config.ghaf.hardware.usb.internal.qemuExtraArgs.fpr0 + ++ optionals config.ghaf.hardware.usb.vhotplug.enableEvdevPassthrough builtins.concatMap (n: [ + "-device" + "pcie-root-port,bus=pcie.0,id=${config.ghaf.hardware.usb.vhotplug.pcieBusPrefix}${toString n},chassis=${toString n}" + ]) (lib.range 1 config.ghaf.hardware.usb.vhotplug.pciePortCount); + }; + }; +} diff --git a/modules/hardware/common/usb/external.nix b/modules/hardware/common/usb/external.nix new file mode 100644 index 000000000..167c1aca4 --- /dev/null +++ b/modules/hardware/common/usb/external.nix @@ -0,0 +1,76 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.hardware.usb.external; + inherit (lib) + mkEnableOption + mkOption + types + mkIf + literalExpression + ; + + # Create USB argument strings for Qemu + qemuExtraArgs = + let + generateArg = + dev: + if ((dev.name != null) && (dev.vendorId != null) && (dev.productId != null)) then + { + name = "${dev.name}"; + value = [ + "-device" + "qemu-xhci" + "-device" + "usb-host,vendorid=0x${dev.vendorId},productid=0x${dev.productId}" + ]; + } + else + builtins.throw "The external USB device is configured incorrectly. Please provide name, vendorId and productId."; + in + builtins.listToAttrs (builtins.map generateArg config.ghaf.hardware.definition.usb.external); + + # Create udev argument strings + extraRules = + let + generateRule = + dev: + if ((dev.vendorId != null) && (dev.productId != null)) then + ''SUBSYSTEM=="usb", ATTR{idVendor}=="${dev.vendorId}", ATTR{idProduct}=="${dev.productId}", GROUP="kvm"'' + else + builtins.throw "The external USB device is configured incorrectly. Please provide name, vendorId and productId."; + in + lib.strings.concatMapStringsSep "\n" generateRule config.ghaf.hardware.definition.usb.external; +in +{ + options.ghaf.hardware.usb.external = { + enable = mkEnableOption "Enable external USB device(s) passthrough support"; + qemuExtraArgs = mkOption { + type = types.attrsOf types.anything; + default = { }; + description = '' + Extra arguments to pass to qemu when enabling the external USB device(s). + Since there can be several devices that may need to be passed to different + machines, the device names are used as keys to access the qemu arguments. + ''; + example = literalExpression '' + { + "device1" = ["-device" "qemu-xhci" "-device" "usb-host,vendorid=0x1234,productid=0x1234"]; + "device2" = ["-device" "qemu-xhci" "-device" "usb-host,vendorid=0x0001,productid=0x0001"]; + } + ''; + }; + }; + + config = mkIf cfg.enable { + ghaf.hardware.usb.external = { + inherit qemuExtraArgs; + }; + + # Host udev rules for external USB devices + services.udev = { + inherit extraRules; + }; + }; +} diff --git a/modules/hardware/common/usb/internal.nix b/modules/hardware/common/usb/internal.nix new file mode 100644 index 000000000..280fc814f --- /dev/null +++ b/modules/hardware/common/usb/internal.nix @@ -0,0 +1,93 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.hardware.usb.internal; + inherit (lib) + mkOption + mkEnableOption + types + mkIf + literalExpression + ; + + # Create USB argument strings for Qemu + qemuExtraArgs = + let + generateArg = + dev: + if ((dev.name != null) && (dev.vendorId != null) && (dev.productId != null)) then + { + name = "${dev.name}"; + value = [ + "-device" + "qemu-xhci" + "-device" + "usb-host,vendorid=0x${dev.vendorId},productid=0x${dev.productId}" + ]; + } + else if ((dev.name != null) && (dev.hostbus != null) && (dev.hostport != null)) then + { + name = "${dev.name}"; + value = [ + "-device" + "qemu-xhci" + "-device" + "usb-host,hostbus=${dev.hostbus},hostport=${dev.hostport}" + ]; + } + else + builtins.throw '' + The internal USB device is configured incorrectly. + Please provide name, and either vendorId and productId or hostbus and hostport.''; + in + builtins.listToAttrs (builtins.map generateArg config.ghaf.hardware.definition.usb.internal); + + # Create udev argument strings + extraRules = + let + generateRule = + dev: + if ((dev.vendorId != null) && (dev.productId != null)) then + ''SUBSYSTEM=="usb", ATTR{idVendor}=="${dev.vendorId}", ATTR{idProduct}=="${dev.productId}", GROUP="kvm"'' + else if ((dev.hostbus != null) && (dev.hostport != null)) then + ''KERNEL=="${dev.hostbus}-${dev.hostport}", SUBSYSTEM=="usb", ATTR{busnum}=="${dev.hostbus}", GROUP="kvm"'' + else + builtins.throw '' + The internal USB device is configured incorrectly. + Please provide name, and either vendorId and productId or hostbus and hostport.''; + in + lib.strings.concatMapStringsSep "\n" generateRule config.ghaf.hardware.definition.usb.internal; +in +{ + options.ghaf.hardware.usb.internal = { + enable = mkEnableOption "Enable internal USB device(s) passthrough support"; + qemuExtraArgs = mkOption { + type = types.attrsOf types.anything; + default = { }; + description = '' + Extra arguments to pass to qemu when enabling the internal USB device(s). + Since there could be several devices that may need to be passed to different + machines, the device names are used as keys to access the qemu arguments. + Note that some devices require special names to be used correctly. + ''; + example = literalExpression '' + { + "device1" = ["-device" "qemu-xhci" "-device" "usb-host,vendorid=0x1234,productid=0x1234"]; + "device2" = ["-device" "qemu-xhci" "-device" "usb-host,vendorid=0x0001,productid=0x0001"]; + } + ''; + }; + }; + + config = mkIf cfg.enable { + # Qemu arguments for internal USB devices + ghaf.hardware.usb.internal = { + inherit qemuExtraArgs; + }; + # Host udev rules for internal USB devices + services.udev = { + inherit extraRules; + }; + }; +} diff --git a/modules/hardware/common/usb/vhotplug.nix b/modules/hardware/common/usb/vhotplug.nix new file mode 100644 index 000000000..d67cc0498 --- /dev/null +++ b/modules/hardware/common/usb/vhotplug.nix @@ -0,0 +1,192 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.ghaf.hardware.usb.vhotplug; + inherit (lib) + mkEnableOption + mkOption + types + mkIf + literalExpression + ; + + vhotplug = pkgs.callPackage ../../../../packages/vhotplug { }; + defaultRules = [ + { + name = "GUIVM"; + qmpSocket = "/var/lib/microvms/gui-vm/gui-vm.sock"; + usbPassthrough = [ + { + class = 3; + protocol = 1; + description = "HID Keyboard"; + } + { + class = 3; + protocol = 2; + description = "HID Mouse"; + } + { + class = 11; + description = "Chip/SmartCard (e.g. YubiKey)"; + } + { + class = 224; + subclass = 1; + protocol = 1; + description = "Bluetooth"; + disable = true; + } + { + # Currently disabled to leave USB drives connected to the host + class = 8; + sublass = 6; + description = "Mass Storage - SCSI (USB drives)"; + disable = true; + } + ]; + evdevPassthrough = { + enable = cfg.enableEvdevPassthrough; + inherit (cfg) pcieBusPrefix; + }; + } + { + name = "NetVM"; + qmpSocket = "/var/lib/microvms/net-vm/net-vm.sock"; + usbPassthrough = [ + { + # Currently disabled to avoid breaking remote nixos-rebuild, + # which requires an Ethernet adapter connected to the host + class = 2; + sublass = 6; + description = "Communications - Ethernet Networking"; + disable = true; + } + ]; + } + { + name = "ChromiumVM"; + qmpSocket = "/var/lib/microvms/chromium-vm/chromium-vm.sock"; + usbPassthrough = [ + { + class = 14; + description = "Video (USB Webcams)"; + } + ]; + } + { + name = "AudioVM"; + qmpSocket = "/var/lib/microvms/audio-vm/audio-vm.sock"; + usbPassthrough = [ + { + class = 1; + description = "Audio"; + } + ]; + } + ]; +in +{ + options.ghaf.hardware.usb.vhotplug = { + enable = mkEnableOption "Enable hot plugging of USB devices"; + + rules = mkOption { + type = types.listOf types.attrs; + default = defaultRules; + description = '' + List of virtual machines with USB hot plugging rules. + ''; + example = literalExpression '' + [ + { + name = "GUIVM"; + qmpSocket = "/var/lib/microvms/gui-vm/gui-vm.sock"; + usbPassthrough = [ + { + class = 3; + protocol = 1; + description = "HID Keyboard"; + ignore = [ + { + vendorId = "046d"; + productId = "c52b"; + description = "Logitech, Inc. Unifying Receiver"; + } + ]; + } + { + vendorId = "067b"; + productId = "23a3"; + description = "Prolific Technology, Inc. USB-Serial Controller"; + disable = true; + } + ]; + } + { + name = "NetVM"; + qmpSocket = "/var/lib/microvms/net-vm/net-vm.sock"; + usbPassthrough = [ + { + productName = ".*ethernet.*"; + description = "Ethernet devices"; + } + ]; + } + ]; + ''; + }; + + enableEvdevPassthrough = mkOption { + description = '' + Enable passthrough of non-USB input devices on startup using QEMU virtio-input-host-pci device. + ''; + type = types.bool; + default = true; + }; + + pcieBusPrefix = mkOption { + type = types.nullOr types.str; + default = "rp"; + description = '' + PCIe bus prefix used for the pcie-root-port QEMU device when evdev passthrough is enabled. + ''; + }; + + pciePortCount = lib.mkOption { + type = lib.types.int; + default = 5; + description = '' + The number of PCIe ports used for hot-plugging virtio-input-host-pci devices. + ''; + }; + }; + + config = mkIf cfg.enable { + services.udev.extraRules = '' + SUBSYSTEM=="usb", GROUP="kvm" + KERNEL=="event*", GROUP="kvm" + ''; + + environment.etc."vhotplug.conf".text = builtins.toJSON { vms = cfg.rules; }; + + systemd.services.vhotplug = { + enable = true; + description = "vhotplug"; + wantedBy = [ "microvms.target" ]; + after = [ "microvms.target" ]; + serviceConfig = { + Type = "simple"; + Restart = "always"; + RestartSec = "1"; + ExecStart = "${vhotplug}/bin/vhotplug -a -c /etc/vhotplug.conf"; + }; + startLimitIntervalSec = 0; + }; + }; +} diff --git a/modules/hardware/definition.nix b/modules/hardware/definition.nix new file mode 100644 index 000000000..642f912ba --- /dev/null +++ b/modules/hardware/definition.nix @@ -0,0 +1,360 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# Module for Hardware Definitions +# +# The point of this module is to only store information about the hardware +# configuration, and the logic that uses this information should be elsewhere. +{ lib, ... }: +let + inherit (lib) mkOption types literalExpression; +in +{ + options.ghaf.hardware.definition = + let + pciDevSubmodule = types.submodule { + options = { + path = mkOption { + type = types.str; + description = '' + PCI device path + ''; + }; + vendorId = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + PCI Vendor ID (optional) + ''; + }; + productId = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + PCI Product ID (optional) + ''; + }; + name = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + PCI device name (optional) + ''; + }; + }; + }; + + # USB device submodule, defined either by product ID and vendor ID, or by bus and port number + usbDevSubmodule = types.submodule { + options = { + name = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + USB device name. NOT optional for external devices, in which case it must not contain spaces + or extravagant characters. + ''; + }; + vendorId = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + USB Vendor ID (optional). If this is set, the productId must also be set. + ''; + }; + productId = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + USB Product ID (optional). If this is set, the vendorId must also be set. + ''; + }; + hostbus = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + USB device bus number (optional). If this is set, the hostport must also be set. + ''; + }; + hostport = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + USB device device number (optional). If this is set, the hostbus must also be set. + ''; + }; + }; + }; + + # Input devices submodule + inputDevSubmodule = types.submodule { + options = { + name = mkOption { + type = types.listOf types.any; + default = [ ]; + description = '' + List of input device names. Can either be a string, or a list of strings. + The list option allows to bind several input device names to the same evdev. + This allows to create one generic hardware definition for multiple SKUs. + ''; + }; + evdev = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + List of event devices. + ''; + }; + }; + }; + + # Kernel configuration submodule + kernelConfig = types.submodule { + options = { + stage1 = { + kernelModules = mkOption { + description = "Hardware specific kernel modules"; + type = types.listOf types.str; + default = [ ]; + example = literalExpression '' + [ + "i915" + ] + ''; + }; + }; + stage2 = { + kernelModules = mkOption { + description = "Hardware specific kernel modules"; + type = types.listOf types.str; + default = [ ]; + example = literalExpression '' + [ + "i915" + ] + ''; + }; + }; + kernelParams = mkOption { + description = "Hardware specific kernel parameters"; + type = types.listOf types.str; + default = [ ]; + example = literalExpression '' + [ + "intel_iommu=on,sm_on" + "iommu=pt" + "module_blacklist=i915" + "acpi_backlight=vendor" + "acpi_osi=linux" + ] + ''; + }; + }; + }; + in + { + name = mkOption { + description = "Name of the hardware"; + type = types.str; + default = ""; + }; + + skus = mkOption { + description = "List of hardware SKUs (Stock Keeping Unit) covered with this definition"; + type = types.listOf types.str; + default = [ ]; + }; + + host = { + kernelConfig = mkOption { + description = "Host kernel configuration"; + type = kernelConfig; + default = { }; + }; + }; + + input = { + keyboard = mkOption { + description = "Name of the keyboard device(s)"; + type = inputDevSubmodule; + default = { }; + }; + + mouse = mkOption { + description = "Name of the mouse device(s)"; + type = inputDevSubmodule; + default = { }; + }; + + touchpad = mkOption { + description = "Name of the touchpad device(s)"; + type = inputDevSubmodule; + default = { }; + }; + + misc = mkOption { + description = "Name of the misc device(s)"; + type = inputDevSubmodule; + default = { }; + }; + }; + + disks = mkOption { + description = "Disks to format and mount"; + type = types.attrsOf ( + types.submodule { + options.device = mkOption { + type = types.str; + description = '' + Path to the disk + ''; + }; + } + ); + default = { }; + example = literalExpression '' + { + disk1.device = "/dev/nvme0n1"; + } + ''; + }; + + network = { + # TODO? Should add NetVM enabler here? + # netvm.enable = mkEnableOption = "NetVM"; + + pciDevices = mkOption { + description = "PCI Devices to passthrough to NetVM"; + type = types.listOf pciDevSubmodule; + default = [ ]; + example = literalExpression '' + [{ + path = "0000:00:14.3"; + vendorId = "8086"; + productId = "51f1"; + }] + ''; + }; + kernelConfig = mkOption { + description = "Hardware specific kernel configuration for network devices"; + type = kernelConfig; + default = { }; + }; + }; + + gpu = { + # TODO? Should add GuiVM enabler here? + # guivm.enable = mkEnableOption = "NetVM"; + + pciDevices = mkOption { + description = "PCI Devices to passthrough to GuiVM"; + type = types.listOf pciDevSubmodule; + default = [ ]; + example = literalExpression '' + [{ + path = "0000:00:02.0"; + vendorId = "8086"; + productId = "a7a1"; + }] + ''; + }; + kernelConfig = mkOption { + description = "Hardware specific kernel configuration for gpu devices"; + type = kernelConfig; + default = { }; + }; + }; + + audio = { + # With the current implementation, the whole PCI IOMMU group 14: + # 00:1f.x in the example from Lenovo X1 Carbon + # must be defined for passthrough to AudioVM + pciDevices = mkOption { + description = "PCI Devices to passthrough to AudioVM"; + type = types.listOf pciDevSubmodule; + default = [ ]; + example = literalExpression '' + [ + { + path = "0000:00:1f.0"; + vendorId = "8086"; + productId = "519d"; + } + { + path = "0000:00:1f.3"; + vendorId = "8086"; + productId = "51ca"; + } + { + path = "0000:00:1f.4"; + vendorId = "8086"; + productId = "51a3"; + } + { + path = "0000:00:1f.5"; + vendorId = "8086"; + productId = "51a4"; + } + ] + ''; + }; + kernelConfig = mkOption { + description = "Hardware specific kernel configuration for audio devices"; + type = kernelConfig; + default = { }; + }; + }; + + usb = { + internal = mkOption { + description = '' + Internal USB device(s) to passthrough. + + Each device definition requires a name, and either vendorId and productId, or hostbus and hostport. + The latter is useful for addressing devices that may have different vendor and product IDs in the + same hardware generation. + + Note that internal devices must follow the naming convention to be correctly identified + and subsequently used. Current special names are: + - 'cam0' for the internal cam0 device + - 'fpr0' for the internal fingerprint reader device + ''; + type = types.listOf usbDevSubmodule; + default = [ ]; + example = literalExpression '' + [ + { + name = "cam0"; + vendorId = "0123"; + productId = "0123"; + } + { + name = "fpr0"; + hostbus = "3"; + hostport = "3"; + } + ] + ''; + }; + external = mkOption { + description = "External USB device(s) to passthrough. Requires name, vendorId, and productId."; + type = types.listOf usbDevSubmodule; + default = [ ]; + example = literalExpression '' + [ + { + name = "external-device-1"; + vendorId = "0123"; + productId = "0123"; + } + { + name = "external-device-2"; + vendorId = "0123"; + productId = "0123"; + } + ] + ''; + }; + }; + }; +} diff --git a/modules/hardware/definitions/dell-latitude/dell-latitude-7230.nix b/modules/hardware/definitions/dell-latitude/dell-latitude-7230.nix new file mode 100644 index 000000000..e4ec45561 --- /dev/null +++ b/modules/hardware/definitions/dell-latitude/dell-latitude-7230.nix @@ -0,0 +1,172 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + # System name + name = "Dell Latitude 7230 Rugged"; + + # List of system SKUs covered by this configuration + skus = [ "0BB7 Latitude 7230 Rugged Extreme Tablet" ]; + + # Host configuration + host = { + kernelConfig.kernelParams = [ + "intel_iommu=on,sm_on" + "iommu=pt" + "acpi_backlight=vendor" + "acpi_osi=linux" + "module_blacklist=i915,iwlwifi,snd_hda_intel,snd_sof_pci_intel_tgl" + ]; + }; + + # Input devices + input = { + keyboard = { + name = [ "AT Translated Set 2 keyboard" ]; + evdev = [ "/dev/keyboard0" ]; + }; + + mouse = { + name = [ + "PS/2 Generic Mouse" + "SYNAPTICS Synaptics HIDUSB TouchPad V1.05 Mouse" + ]; + evdev = [ + "/dev/mouse0" + "/dev/mouse1" + ]; + }; + + touchpad = { + name = [ + "SYNAPTICS Synaptics HIDUSB TouchPad V1.05 Touchpad" + "EETI8082:00 0EEF:C004" + "EETI8082:00 0EEF:C004 Stylus" + ]; + evdev = [ + "/dev/touchpad0" + "/dev/touchpad1" + "/dev/touchpad2" + ]; + }; + + misc = { + name = [ + # "Intel HID events" "Dell WMI hotkeys" "Video Bus" "HDA Intel PCH Headphone Mic" "HDA Intel PCH HDMI/DP,pcm=3" "HDA Intel PCH HDMI/DP,pcm=7" "HDA Intel PCH HDMI/DP,pcm=8" "HDA Intel PCH HDMI/DP,pcm=9" "Intel HID 5 button array" "Lid Switch" "Power Button" "Sleep Button" + ]; + evdev = [ + # /dev/input/by-path/platform-INTC1070:00-event /dev/input/by-path/platform-PNP0C14:02-event + ]; + }; + }; + + # Main disk device + disks = { + disk1.device = "/dev/nvme0n1"; + }; + + # Network devices for passthrough to netvm + network = { + pciDevices = [ + { + # Network controller: Intel Corporation Alder Lake-P PCH CNVi WiFi (rev 01) + name = "wlp0s5f0"; + path = "0000:00:14.3"; + vendorId = "8086"; + productId = "51f0"; + # Detected kernel driver: iwlwifi + # Detected kernel modules: iwlwifi + } + ]; + kernelConfig = { + stage1.kernelModules = [ ]; + stage2.kernelModules = [ "iwlwifi" ]; + kernelParams = [ ]; + }; + }; + + # GPU devices for passthrough to guivm + gpu = { + pciDevices = [ + { + # VGA compatible controller: Intel Corporation Alder Lake-UP4 GT2 [Iris Xe Graphics] (rev 0c) + name = "gpu0"; + path = "0000:00:02.0"; + vendorId = "8086"; + productId = "46aa"; + # Detected kernel driver: i915 + # Detected kernel modules: i915 + } + ]; + kernelConfig = { + stage1.kernelModules = [ "i915" ]; + stage2.kernelModules = [ ]; + kernelParams = [ "earlykms" ]; + }; + }; + + # Audio device for passthrough to audiovm + audio = { + pciDevices = [ + { + # ISA bridge: Intel Corporation Alder Lake LPC Controller (rev 01) + name = "snd0-0"; + path = "0000:00:1f.0"; + vendorId = "8086"; + productId = "5187"; + # Detected kernel driver: + # Detected kernel modules: + } + { + # Serial bus controller: Intel Corporation Alder Lake-P PCH SPI Controller (rev 01) + name = "snd0-1"; + path = "0000:00:1f.5"; + vendorId = "8086"; + productId = "51a4"; + # Detected kernel driver: intel-spi + # Detected kernel modules: spi_intel_pci + } + { + # Audio device: Intel Corporation Alder Lake Smart Sound Technology Audio Controller (rev 01) + name = "snd0-2"; + path = "0000:00:1f.3"; + vendorId = "8086"; + productId = "51cc"; + # Detected kernel driver: snd_hda_intel + # Detected kernel modules: snd_hda_intel,snd_sof_pci_intel_tgl + } + { + # SMBus: Intel Corporation Alder Lake PCH-P SMBus Host Controller (rev 01) + name = "snd0-3"; + path = "0000:00:1f.4"; + vendorId = "8086"; + productId = "51a3"; + # Detected kernel driver: i801_smbus + # Detected kernel modules: i2c_i801 + } + ]; + kernelConfig = { + stage1.kernelModules = [ ]; + stage2.kernelModules = [ + "i2c_i801" + "snd_hda_intel" + "snd_sof_pci_intel_tgl" + "spi_intel_pci" + ]; + kernelParams = [ ]; + }; + }; + + # USB devices for passthrough + usb = { + internal = [ + { + name = "gps0"; + hostbus = "3"; + hostport = "7"; + } + ]; + external = [ + # Add external USB devices here + ]; + }; +} diff --git a/modules/hardware/definitions/dell-latitude/dell-latitude-7330.nix b/modules/hardware/definitions/dell-latitude/dell-latitude-7330.nix new file mode 100644 index 000000000..fe294a679 --- /dev/null +++ b/modules/hardware/definitions/dell-latitude/dell-latitude-7330.nix @@ -0,0 +1,185 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + # System name + name = "Dell Inc. Not Specified"; + + # List of system SKUs covered by this configuration + skus = [ "0A9E Latitude 7330 Rugged Extreme" ]; + + # Host configuration + host = { + kernelConfig.kernelParams = [ + "intel_iommu=on,sm_on" + "iommu=pt" + "acpi_backlight=vendor" + "acpi_osi=linux" + #"module_blacklist=e1000e,i2c_i801,i915,iwlwifi,snd_hda_intel,snd_sof_pci_intel_tgl,spi_intel_pci" + ]; + }; + + # Input devices + input = { + keyboard = { + name = [ + "AT Translated Set 2 keyboard" + "DELL0A9E:00 214A:0028 Keyboard" + ]; + evdev = [ + "/dev/keyboard0" + "/dev/keyboard1" + ]; + }; + + mouse = { + name = [ + "DELL0A9E:00 214A:0028 Mouse" + "PS/2 Generic Mouse" + ]; + evdev = [ + "/dev/mouse0" + "/dev/mouse1" + ]; + }; + + touchpad = { + name = [ "CUST0000:00 0EEF:C003" ]; + evdev = [ "/dev/touchpad0" ]; + }; + + misc = { + name = [ + # "Lid Switch" "Video Bus" "HDA Intel PCH Headphone Mic" "HDA Intel PCH HDMI/DP,pcm=3" "HDA Intel PCH HDMI/DP,pcm=7" "HDA Intel PCH HDMI/DP,pcm=8" "HDA Intel PCH HDMI/DP,pcm=9" "Power Button" "Sleep Button" "Intel HID events" "Intel HID 5 button array" "Dell WMI hotkeys" + ]; + evdev = [ + # /dev/input/by-path/platform-INTC1051:00-event /dev/input/by-path/platform-PNP0C14:02-event + ]; + }; + }; + + # Main disk device + disks = { + disk1.device = "/dev/nvme0n1"; + }; + + # Network devices for passthrough to netvm + network = { + #TODO Add the Ethernet device + pciDevices = [ + { + # Network controller: Intel Corporation Wi-Fi 6E(802.11ax) AX210/AX1675* 2x2 [Typhoon Peak] (rev 1a) + name = "wlp0s5f0"; + path = "0000:72:00.0"; + vendorId = "8086"; + productId = "2725"; + # Detected kernel driver: iwlwifi + # Detected kernel modules: iwlwifi + } + ]; + kernelConfig = { + # Kernel modules are indicative only, please investigate with lsmod/modinfo + stage1.kernelModules = [ ]; + stage2.kernelModules = [ "iwlwifi" ]; + kernelParams = [ ]; + }; + }; + + # GPU devices for passthrough to guivm + gpu = { + pciDevices = [ + { + # VGA compatible controller: Intel Corporation TigerLake-LP GT2 [Iris Xe Graphics] (rev 01) + name = "gpu0"; + path = "0000:00:02.0"; + vendorId = "8086"; + productId = "9a49"; + # Detected kernel driver: i915 + # Detected kernel modules: i915 + } + ]; + kernelConfig = { + # Kernel modules are indicative only, please investigate with lsmod/modinfo + stage1.kernelModules = [ "i915" ]; + stage2.kernelModules = [ ]; + kernelParams = [ "earlykms" ]; + }; + }; + + # Audio device for passthrough to audiovm + audio = { + #TODO: Fix splitting the Ethernet from the Audio iommu + pciDevices = [ + { + # ISA bridge: Intel Corporation Tiger Lake-LP LPC Controller (rev 20) + name = "snd0-0"; + path = "0000:00:1f.0"; + vendorId = "8086"; + productId = "a082"; + # Detected kernel driver: + # Detected kernel modules: + } + { + # Serial bus controller: Intel Corporation Tiger Lake-LP SPI Controller (rev 20) + name = "snd0-1"; + path = "0000:00:1f.5"; + vendorId = "8086"; + productId = "a0a4"; + # Detected kernel driver: intel-spi + # Detected kernel modules: spi_intel_pci + } + { + # Audio device: Intel Corporation Tiger Lake-LP Smart Sound Technology Audio Controller (rev 20) + name = "snd0-2"; + path = "0000:00:1f.3"; + vendorId = "8086"; + productId = "a0c8"; + # Detected kernel driver: snd_hda_intel + # Detected kernel modules: snd_hda_intel,snd_sof_pci_intel_tgl + } + { + # Ethernet controller: Intel Corporation Ethernet Connection (13) I219-LM (rev 20) + name = "snd0-3"; + path = "0000:00:1f.6"; + vendorId = "8086"; + productId = "15fb"; + # Detected kernel driver: e1000e + # Detected kernel modules: e1000e + } + { + # SMBus: Intel Corporation Tiger Lake-LP SMBus Controller (rev 20) + name = "snd0-4"; + path = "0000:00:1f.4"; + vendorId = "8086"; + productId = "a0a3"; + # Detected kernel driver: i801_smbus + # Detected kernel modules: i2c_i801 + } + ]; + kernelConfig = { + # Kernel modules are indicative only, please investigate with lsmod/modinfo + stage1.kernelModules = [ ]; + stage2.kernelModules = [ + "e1000e" + "i2c_i801" + "snd_hda_intel" + "snd_sof_pci_intel_tgl" + "spi_intel_pci" + ]; + kernelParams = [ ]; + }; + }; + + # USB devices for passthrough + usb = { + internal = [ + { + name = "cam0"; + hostbus = "3"; + hostport = "6"; + } + ]; + external = [ + # Add external USB devices here + ]; + }; +} diff --git a/modules/hardware/flake-module.nix b/modules/hardware/flake-module.nix new file mode 100644 index 000000000..5dd2742bf --- /dev/null +++ b/modules/hardware/flake-module.nix @@ -0,0 +1,18 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ inputs, ... }: +{ + flake.nixosModules = { + laptop.imports = [ + ./definition.nix + ./x86_64-generic + ./laptop.nix + ./common + { nixpkgs.overlays = [ inputs.ghafpkgs.overlays.default ]; } + ]; + hw-x86_64-generic.imports = [ + ./definition.nix + ./x86_64-generic + ]; + }; +} diff --git a/modules/hardware/laptop.nix b/modules/hardware/laptop.nix new file mode 100644 index 000000000..bf538fe9a --- /dev/null +++ b/modules/hardware/laptop.nix @@ -0,0 +1,71 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ config, lib, ... }: +let + inherit (builtins) toString typeOf; + inherit (lib) + mkOption + types + concatImapStrings + concatMapStringsSep + ; + + cfg = config.ghaf.hardware.definition; + hwDefinition = import (./. + cfg.configFile); + + # Helper function to create udev rules for input devices + generateUdevRules = + devlink: deviceList: + concatImapStrings ( + i: d: + if (typeOf d) == "list" then + ''${ + concatMapStringsSep "\n" ( + sd: + ''SUBSYSTEM=="input", ATTRS{name}=="${sd}", KERNEL=="event*", GROUP="kvm", SYMLINK+="${devlink}${toString (i - 1)}"'' + ) d + }''\n'' + else + ''SUBSYSTEM=="input", ATTRS{name}=="${d}", KERNEL=="event*", GROUP="kvm", SYMLINK+="${devlink}${toString (i - 1)}"''\n'' + ) deviceList; +in +{ + imports = [ ./definition.nix ]; + + options.ghaf.hardware.definition.configFile = mkOption { + description = "Path to the hardware configuration file."; + type = types.str; + default = ""; + }; + + config = { + # Hardware definition + ghaf.hardware.definition = { + inherit (hwDefinition) host; + inherit (hwDefinition) input; + inherit (hwDefinition) disks; + inherit (hwDefinition) network; + inherit (hwDefinition) gpu; + inherit (hwDefinition) audio; + inherit (hwDefinition) usb; + }; + + # Disk configuration + disko.devices.disk = hwDefinition.disks; + + # Host udev rules for input devices + services.udev.extraRules = '' + # Keyboard + ${generateUdevRules "keyboard" hwDefinition.input.keyboard.name} + # Mouse + ${generateUdevRules "mouse" hwDefinition.input.mouse.name} + # Touchpad + ${generateUdevRules "touchpad" hwDefinition.input.touchpad.name} + # Misc + ${lib.strings.concatMapStringsSep "\n" ( + d: ''SUBSYSTEM=="input", ATTRS{name}=="${d}", GROUP="kvm"'' + ) hwDefinition.input.misc.name} + ''; + }; +} diff --git a/modules/hardware/lenovo-x1/definitions/x1-gen10.nix b/modules/hardware/lenovo-x1/definitions/x1-gen10.nix new file mode 100644 index 000000000..82f3139c7 --- /dev/null +++ b/modules/hardware/lenovo-x1/definitions/x1-gen10.nix @@ -0,0 +1,150 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + # System name + name = "Lenovo X1 Carbon Gen 10"; + + # List of system SKUs covered by this configuration + skus = [ + # TODO Add SKUs + ]; + + host = { + kernelConfig.kernelParams = [ + "intel_iommu=on,sm_on" + "iommu=pt" + "module_blacklist=i915" # Prevent i915 module from being accidentally used by host + "acpi_backlight=vendor" + "acpi_osi=linux" + ]; + }; + + input = { + keyboard = { + name = [ "AT Translated Set 2 keyboard" ]; + evdev = [ "/dev/input/by-path/platform-i8042-serio-0-event-kbd" ]; + }; + + mouse = { + name = [ + [ + "ELAN067B:00 04F3:31F8 Mouse" + "SYNA8016:00 06CB:CEB3 Mouse" + ] + "TPPS/2 Elan TrackPoint" + ]; + evdev = [ + "/dev/mouse0" + "/dev/mouse1" + ]; + }; + + touchpad = { + name = [ + [ + "ELAN067B:00 04F3:31F8 Touchpad" + "SYNA8016:00 06CB:CEB3 Touchpad" + ] + ]; + evdev = [ "/dev/touchpad0" ]; + }; + + misc = { + name = [ "ThinkPad Extra Buttons" ]; + evdev = [ "/dev/input/by-path/platform-thinkpad_acpi-event" ]; + }; + }; + + disks = { + disk1.device = "/dev/nvme0n1"; + }; + + network.pciDevices = [ + { + # Passthrough Intel WiFi card + path = "0000:00:14.3"; + vendorId = "8086"; + productId = "51f0"; + name = "wlp0s5f0"; + } + ]; + + gpu = { + pciDevices = [ + { + # Passthrough Intel Iris GPU + path = "0000:00:02.0"; + vendorId = "8086"; + productId = "46a6"; + } + ]; + kernelConfig = { + stage1.kernelModules = [ "i915" ]; + kernelParams = [ "earlykms" ]; + }; + }; + + # With the current implementation, the whole PCI IOMMU group 13: + # 00:1f.x in the Lenovo X1 Carbon 10 gen + # must be defined for passthrough to AudioVM + audio = { + pciDevices = [ + { + # ISA bridge: Intel Corporation Alder Lake PCH eSPI Controller(rev 01) + path = "0000:00:1f.0"; + vendorId = "8086"; + productId = "5182"; + } + { + # Audio device: Intel Corporation Alder Lake PCH-P High Definition Audio Controller (rev 01) + path = "0000:00:1f.3"; + vendorId = "8086"; + productId = "51c8"; + } + { + # SMBus: Intel Corporation Alder Lake PCH-P SMBus Host Controller (rev 01) + path = "0000:00:1f.4"; + vendorId = "8086"; + productId = "51a3"; + } + { + # Serial bus controller: Intel Corporation Alder Lake-P PCH SPI Controller (rev 01) + path = "0000:00:1f.5"; + vendorId = "8086"; + productId = "51a4"; + } + ]; + kernelConfig.kernelParams = [ + "snd_intel_dspcfg.dsp_driver=3" + "snd_sof_intel_hda_common.dmic_num=4" + ]; + }; + + usb = { + internal = [ + { + name = "cam0"; + hostbus = "3"; + hostport = "8"; + } + { + name = "fpr0"; + hostbus = "3"; + hostport = "6"; + } + ]; + external = [ + { + name = "gps0"; + vendorId = "067b"; + productId = "23a3"; + } + { + name = "yubikey"; + vendorId = "1050"; + productId = "0407"; + } + ]; + }; +} diff --git a/modules/hardware/lenovo-x1/definitions/x1-gen11.nix b/modules/hardware/lenovo-x1/definitions/x1-gen11.nix new file mode 100644 index 000000000..ea9e01323 --- /dev/null +++ b/modules/hardware/lenovo-x1/definitions/x1-gen11.nix @@ -0,0 +1,153 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + # System name + name = "Lenovo X1 Carbon Gen 11"; + + # List of system SKUs covered by this configuration + skus = [ + "LENOVO_MT_21HM_BU_Think_FM_ThinkPad X1 Carbon Gen 11 21HM006EGR" + # TODO Add more SKUs + ]; + + host = { + kernelConfig.kernelParams = [ + "intel_iommu=on,sm_on" + "iommu=pt" + "module_blacklist=i915" # Prevent i915 module from being accidentally used by host + "acpi_backlight=vendor" + "acpi_osi=linux" + ]; + }; + + input = { + keyboard = { + name = [ "AT Translated Set 2 keyboard" ]; + evdev = [ "/dev/input/by-path/platform-i8042-serio-0-event-kbd" ]; + }; + + mouse = { + name = [ + [ + "ELAN067C:00 04F3:31F9 Mouse" + "SYNA8016:00 06CB:CEB3 Mouse" + "ELAN067B:00 04F3:31F8 Mouse" + ] + "TPPS/2 Elan TrackPoint" + ]; + evdev = [ + "/dev/mouse0" + "/dev/mouse1" + ]; + }; + + touchpad = { + name = [ + [ + "ELAN067C:00 04F3:31F9 Touchpad" + "SYNA8016:00 06CB:CEB3 Touchpad" + "ELAN067B:00 04F3:31F8 Touchpad" + ] + ]; + evdev = [ "/dev/touchpad0" ]; + }; + + misc = { + name = [ "ThinkPad Extra Buttons" ]; + evdev = [ "/dev/input/by-path/platform-thinkpad_acpi-event" ]; + }; + }; + + disks = { + disk1.device = "/dev/nvme0n1"; + }; + + network.pciDevices = [ + { + # Passthrough Intel WiFi card + path = "0000:00:14.3"; + vendorId = "8086"; + productId = "51f1"; + name = "wlp0s5f0"; + } + ]; + + gpu = { + pciDevices = [ + { + # Passthrough Intel Iris GPU + path = "0000:00:02.0"; + vendorId = "8086"; + productId = "a7a1"; + } + ]; + kernelConfig = { + stage1.kernelModules = [ "i915" ]; + kernelParams = [ "earlykms" ]; + }; + }; + + # With the current implementation, the whole PCI IOMMU group 14: + # 00:1f.x in the example from Lenovo X1 Carbon + # must be defined for passthrough to AudioVM + audio = { + pciDevices = [ + { + # ISA bridge: Intel Corporation Raptor Lake LPC/eSPI Controller (rev 01) + path = "0000:00:1f.0"; + vendorId = "8086"; + productId = "519d"; + } + { + # Audio device: Intel Corporation Raptor Lake-P/U/H cAVS (rev 01) + path = "0000:00:1f.3"; + vendorId = "8086"; + productId = "51ca"; + } + { + # SMBus: Intel Corporation Alder Lake PCH-P SMBus Host Controller (rev 01) + path = "0000:00:1f.4"; + vendorId = "8086"; + productId = "51a3"; + } + { + # Serial bus controller: Intel Corporation Alder Lake-P PCH SPI Controller (rev 01) + path = "0000:00:1f.5"; + vendorId = "8086"; + productId = "51a4"; + } + ]; + kernelConfig.kernelParams = [ + "snd_intel_dspcfg.dsp_driver=3" + "snd_sof_intel_hda_common.dmic_num=4" + ]; + }; + + usb = { + internal = [ + { + name = "cam0"; + hostbus = "3"; + hostport = "8"; + } + { + name = "fpr0"; + hostbus = "3"; + hostport = "6"; + } + ]; + external = [ + { + name = "gps0"; + vendorId = "067b"; + productId = "23a3"; + } + { + name = "yubikey"; + vendorId = "1050"; + productId = "0407"; + } + ]; + }; +} diff --git a/modules/hardware/lenovo-x1/kernel/guest/test/default.nix b/modules/hardware/lenovo-x1/kernel/guest/test/default.nix new file mode 100644 index 000000000..5e747d15b --- /dev/null +++ b/modules/hardware/lenovo-x1/kernel/guest/test/default.nix @@ -0,0 +1,7 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ pkgs, ... }: +let + config = pkgs.nixos [ ./test-configuration.nix ]; +in +config.config.system.build.toplevel diff --git a/modules/hardware/lenovo-x1/kernel/guest/test/test-configuration.nix b/modules/hardware/lenovo-x1/kernel/guest/test/test-configuration.nix new file mode 100644 index 000000000..b91f68595 --- /dev/null +++ b/modules/hardware/lenovo-x1/kernel/guest/test/test-configuration.nix @@ -0,0 +1,40 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ lib, ... }: +{ + imports = [ + ../../../../x86_64-generic/kernel/host/default.nix + ../../../../x86_64-generic/kernel/guest/default.nix + ]; + + config = { + # baseline, virtualization and network hardening are + # generic to all x86_64 devices + ghaf = { + host.kernel.hardening = { + enable = true; + virtualization.enable = true; + networking.enable = true; + inputdevices.enable = true; + # usb/debug hardening is host optional but required for -debug builds + usb.enable = true; + debug.enable = true; + }; + # guest VM kernel specific options + guest.kernel.hardening = { + enable = true; + graphics.enable = true; + }; + }; + + # required to module test a module via top level configuration + boot.loader.systemd-boot.enable = true; + + fileSystems."/" = { + device = "/dev/disk/by-uuid/00000000-0000-0000-0000-000000000000"; + fsType = "ext4"; + }; + + system.stateVersion = lib.trivial.release; + }; +} diff --git a/modules/common/hardware/x86_64-generic/default.nix b/modules/hardware/x86_64-generic/default.nix similarity index 81% rename from modules/common/hardware/x86_64-generic/default.nix rename to modules/hardware/x86_64-generic/default.nix index 6ce0c0f5a..c09c4496b 100644 --- a/modules/common/hardware/x86_64-generic/default.nix +++ b/modules/hardware/x86_64-generic/default.nix @@ -6,5 +6,7 @@ ./kernel/hardening.nix ./kernel/host ./kernel/host/pkvm + ./x86_64-linux.nix + ./modules/tpm2.nix ]; } diff --git a/modules/common/hardware/x86_64-generic/kernel/configs/ghaf_host_hardened_baseline-x86 b/modules/hardware/x86_64-generic/kernel/configs/ghaf_host_hardened_baseline-x86 similarity index 100% rename from modules/common/hardware/x86_64-generic/kernel/configs/ghaf_host_hardened_baseline-x86 rename to modules/hardware/x86_64-generic/kernel/configs/ghaf_host_hardened_baseline-x86 diff --git a/modules/common/hardware/x86_64-generic/kernel/guest/configs/display-gpu.config b/modules/hardware/x86_64-generic/kernel/guest/configs/display-gpu.config similarity index 100% rename from modules/common/hardware/x86_64-generic/kernel/guest/configs/display-gpu.config rename to modules/hardware/x86_64-generic/kernel/guest/configs/display-gpu.config diff --git a/modules/common/hardware/x86_64-generic/kernel/guest/configs/guest.config b/modules/hardware/x86_64-generic/kernel/guest/configs/guest.config similarity index 100% rename from modules/common/hardware/x86_64-generic/kernel/guest/configs/guest.config rename to modules/hardware/x86_64-generic/kernel/guest/configs/guest.config diff --git a/modules/hardware/x86_64-generic/kernel/guest/default.nix b/modules/hardware/x86_64-generic/kernel/guest/default.nix new file mode 100644 index 000000000..9d46dfde3 --- /dev/null +++ b/modules/hardware/x86_64-generic/kernel/guest/default.nix @@ -0,0 +1,18 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ lib, ... }: +{ + options.ghaf.guest.kernel.hardening = { + enable = lib.mkOption { + description = "Enable Ghaf Guest hardening feature"; + type = lib.types.bool; + default = false; + }; + + graphics.enable = lib.mkOption { + description = "Enable support for Graphics in the Ghaf Guest"; + type = lib.types.bool; + default = false; + }; + }; +} diff --git a/modules/hardware/x86_64-generic/kernel/hardening.nix b/modules/hardware/x86_64-generic/kernel/hardening.nix new file mode 100644 index 000000000..f15623a00 --- /dev/null +++ b/modules/hardware/x86_64-generic/kernel/hardening.nix @@ -0,0 +1,37 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ ... }: +{ + imports = [ + ./host + ./guest + ./host/pkvm + # other host hardening modules - to be defined later + ]; + + config = { + # host kernel hardening + ghaf = { + host = { + kernel.hardening = { + enable = false; + virtualization.enable = false; + networking.enable = false; + usb.enable = false; + inputdevices.enable = false; + debug.enable = false; + # host kernel hypervisor (KVM) hardening + hypervisor.enable = false; + }; + }; + # guest kernel hardening + guest = { + kernel.hardening = { + enable = false; + graphics.enable = false; + }; + }; + # other host hardening options - user space, etc. - to be defined later + }; + }; +} diff --git a/modules/common/hardware/x86_64-generic/kernel/host/configs/debug.config b/modules/hardware/x86_64-generic/kernel/host/configs/debug.config similarity index 100% rename from modules/common/hardware/x86_64-generic/kernel/host/configs/debug.config rename to modules/hardware/x86_64-generic/kernel/host/configs/debug.config diff --git a/modules/common/hardware/x86_64-generic/kernel/host/configs/networking.config b/modules/hardware/x86_64-generic/kernel/host/configs/networking.config similarity index 100% rename from modules/common/hardware/x86_64-generic/kernel/host/configs/networking.config rename to modules/hardware/x86_64-generic/kernel/host/configs/networking.config diff --git a/modules/common/hardware/x86_64-generic/kernel/host/configs/usb.config b/modules/hardware/x86_64-generic/kernel/host/configs/usb.config similarity index 100% rename from modules/common/hardware/x86_64-generic/kernel/host/configs/usb.config rename to modules/hardware/x86_64-generic/kernel/host/configs/usb.config diff --git a/modules/common/hardware/x86_64-generic/kernel/host/configs/user-input-devices.config b/modules/hardware/x86_64-generic/kernel/host/configs/user-input-devices.config similarity index 100% rename from modules/common/hardware/x86_64-generic/kernel/host/configs/user-input-devices.config rename to modules/hardware/x86_64-generic/kernel/host/configs/user-input-devices.config diff --git a/modules/common/hardware/x86_64-generic/kernel/host/configs/virtualization.config b/modules/hardware/x86_64-generic/kernel/host/configs/virtualization.config similarity index 100% rename from modules/common/hardware/x86_64-generic/kernel/host/configs/virtualization.config rename to modules/hardware/x86_64-generic/kernel/host/configs/virtualization.config diff --git a/modules/common/hardware/x86_64-generic/kernel/host/default.nix b/modules/hardware/x86_64-generic/kernel/host/default.nix similarity index 54% rename from modules/common/hardware/x86_64-generic/kernel/host/default.nix rename to modules/hardware/x86_64-generic/kernel/host/default.nix index 1a2cc2cb9..ba3fd004c 100644 --- a/modules/common/hardware/x86_64-generic/kernel/host/default.nix +++ b/modules/hardware/x86_64-generic/kernel/host/default.nix @@ -5,62 +5,64 @@ lib, pkgs, ... -}: let +}: +let + inherit (lib) types mkOption mkIf; + # Importing kernel builder function from packages and checking hardening options - buildKernel = import ../../../../../../packages/kernel {inherit config pkgs lib;}; + buildKernel = import ../../../../../packages/kernel { inherit config pkgs lib; }; config_baseline = ../configs/ghaf_host_hardened_baseline-x86; host_hardened_kernel = buildKernel { inherit config_baseline; host_build = true; }; - enable_kernel_hardening = config.ghaf.host.kernel.hardening.enable; + cfg = config.ghaf.host.kernel.hardening; in - with lib; { - options.ghaf.host.kernel.hardening.enable = mkOption { +{ + options.ghaf.host.kernel.hardening = { + enable = mkOption { description = "Enable Ghaf Host hardening feature"; type = types.bool; default = false; }; - options.ghaf.host.kernel.hardening.virtualization.enable = mkOption { + virtualization.enable = mkOption { description = "Enable support for virtualization in the Ghaf Host"; type = types.bool; default = false; }; - options.ghaf.host.kernel.hardening.networking.enable = mkOption { + networking.enable = mkOption { description = "Enable support for networking in the Ghaf Host"; type = types.bool; default = false; }; - options.ghaf.host.kernel.hardening.usb.enable = mkOption { + usb.enable = mkOption { description = "Enable support for USB in the Ghaf Host"; type = types.bool; default = false; }; - options.ghaf.host.kernel.hardening.inputdevices.enable = mkOption { + inputdevices.enable = mkOption { description = "Enable support for input devices in the Ghaf Host"; type = types.bool; default = false; }; - options.ghaf.host.kernel.hardening.debug.enable = mkOption { + debug.enable = mkOption { description = "Enable support for debug features in the Ghaf Host"; type = types.bool; default = false; }; + }; - config = mkIf enable_kernel_hardening { - boot.kernelPackages = pkgs.linuxPackagesFor host_hardened_kernel; - # https://github.com/NixOS/nixpkgs/issues/109280#issuecomment-973636212 - nixpkgs.overlays = [ - (_final: prev: { - makeModulesClosure = x: - prev.makeModulesClosure (x // {allowMissing = true;}); - }) - ]; - }; - } + config = mkIf cfg.enable { + boot.kernelPackages = pkgs.linuxPackagesFor host_hardened_kernel; + # https://github.com/NixOS/nixpkgs/issues/109280#issuecomment-973636212 + nixpkgs.overlays = [ + (_final: prev: { makeModulesClosure = x: prev.makeModulesClosure (x // { allowMissing = true; }); }) + ]; + }; +} diff --git a/modules/common/hardware/x86_64-generic/kernel/host/pkvm/default.nix b/modules/hardware/x86_64-generic/kernel/host/pkvm/default.nix similarity index 64% rename from modules/common/hardware/x86_64-generic/kernel/host/pkvm/default.nix rename to modules/hardware/x86_64-generic/kernel/host/pkvm/default.nix index f47651f59..9ccf91bc5 100644 --- a/modules/common/hardware/x86_64-generic/kernel/host/pkvm/default.nix +++ b/modules/hardware/x86_64-generic/kernel/host/pkvm/default.nix @@ -5,7 +5,8 @@ lib, pkgs, ... -}: let +}: +let pkvmKernel = pkgs.linux_6_1.override { argsOverride = rec { src = pkgs.fetchurl { @@ -20,7 +21,7 @@ pkvm_patch = [ { name = "pkvm-patch"; - patch = ../../../../../virtualization/pkvm/0001-pkvm-enable-pkvm-on-intel-x86-6.1-lts.patch; + patch = ../../../../../common/virtualization/pkvm/0001-pkvm-enable-pkvm-on-intel-x86-6.1-lts.patch; structuredExtraConfig = with lib.kernel; { KVM_INTEL = yes; KSM = no; @@ -35,14 +36,14 @@ hyp_cfg = config.ghaf.host.kernel.hardening.hypervisor; in - with lib; { - options.ghaf.host.kernel.hardening.hypervisor.enable = mkOption { - description = "Enable Hypervisor hardening feature"; - type = types.bool; - default = false; - }; - config = mkIf hyp_cfg.enable { - boot.kernelPackages = pkgs.linuxPackagesFor pkvmKernel; - boot.kernelPatches = pkvm_patch; - }; - } +{ + options.ghaf.host.kernel.hardening.hypervisor.enable = lib.mkOption { + description = "Enable Hypervisor hardening feature"; + type = lib.types.bool; + default = false; + }; + config = lib.mkIf hyp_cfg.enable { + boot.kernelPackages = pkgs.linuxPackagesFor pkvmKernel; + boot.kernelPatches = pkvm_patch; + }; +} diff --git a/modules/hardware/x86_64-generic/kernel/host/pkvm/test/default.nix b/modules/hardware/x86_64-generic/kernel/host/pkvm/test/default.nix new file mode 100644 index 000000000..5e747d15b --- /dev/null +++ b/modules/hardware/x86_64-generic/kernel/host/pkvm/test/default.nix @@ -0,0 +1,7 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ pkgs, ... }: +let + config = pkgs.nixos [ ./test-configuration.nix ]; +in +config.config.system.build.toplevel diff --git a/modules/hardware/x86_64-generic/kernel/host/pkvm/test/test-configuration.nix b/modules/hardware/x86_64-generic/kernel/host/pkvm/test/test-configuration.nix new file mode 100644 index 000000000..fbc6d75f4 --- /dev/null +++ b/modules/hardware/x86_64-generic/kernel/host/pkvm/test/test-configuration.nix @@ -0,0 +1,19 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ lib, ... }: +{ + imports = [ ../default.nix ]; + + # pkvm hardening is generic to all x86_64 devices + config = { + ghaf.host.kernel.hardening.hypervisor.enable = true; + + # required to module test a module via top level configuration + boot.loader.systemd-boot.enable = true; + fileSystems."/" = { + device = "/dev/disk/by-uuid/00000000-0000-0000-0000-000000000000"; + fsType = "ext4"; + }; + system.stateVersion = lib.trivial.release; + }; +} diff --git a/modules/hardware/x86_64-generic/kernel/host/test/default.nix b/modules/hardware/x86_64-generic/kernel/host/test/default.nix new file mode 100644 index 000000000..5e747d15b --- /dev/null +++ b/modules/hardware/x86_64-generic/kernel/host/test/default.nix @@ -0,0 +1,7 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ pkgs, ... }: +let + config = pkgs.nixos [ ./test-configuration.nix ]; +in +config.config.system.build.toplevel diff --git a/modules/hardware/x86_64-generic/kernel/host/test/test-configuration.nix b/modules/hardware/x86_64-generic/kernel/host/test/test-configuration.nix new file mode 100644 index 000000000..ff8005f0e --- /dev/null +++ b/modules/hardware/x86_64-generic/kernel/host/test/test-configuration.nix @@ -0,0 +1,31 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ lib, ... }: +{ + imports = [ + ../default.nix + # import guest also to bring the defaults (false) to scope + ../../guest/default.nix + ]; + + # baseline, virtualization and network hardening are + # generic to all x86_64 devices + config = { + ghaf.host.kernel.hardening = { + enable = true; + virtualization.enable = true; + networking.enable = true; + inputdevices.enable = true; + # usb/debug hardening is host optional but required for -debug builds + usb.enable = true; + debug.enable = true; + }; + # required to module test a module via top level configuration + boot.loader.systemd-boot.enable = true; + fileSystems."/" = { + device = "/dev/disk/by-uuid/00000000-0000-0000-0000-000000000000"; + fsType = "ext4"; + }; + system.stateVersion = lib.trivial.release; + }; +} diff --git a/modules/hardware/x86_64-generic/modules/tpm2.nix b/modules/hardware/x86_64-generic/modules/tpm2.nix new file mode 100644 index 000000000..4b6368a62 --- /dev/null +++ b/modules/hardware/x86_64-generic/modules/tpm2.nix @@ -0,0 +1,36 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.ghaf.hardware.tpm2; +in +{ + options.ghaf.hardware.tpm2 = { + enable = lib.mkEnableOption "TPM2 PKCS#11 interface"; + }; + + config = lib.mkIf cfg.enable { + security.tpm2 = { + enable = true; + pkcs11.enable = true; + abrmd.enable = true; + }; + + environment.systemPackages = lib.mkIf config.ghaf.profiles.debug.enable [ + pkgs.opensc + pkgs.tpm2-tools + ]; + + assertions = [ + { + assertion = pkgs.stdenv.isx86_64; + message = "TPM2 is only supported on x86_64"; + } + ]; + }; +} diff --git a/modules/hardware/x86_64-generic/x86_64-linux.nix b/modules/hardware/x86_64-generic/x86_64-linux.nix new file mode 100644 index 000000000..8b80b3739 --- /dev/null +++ b/modules/hardware/x86_64-generic/x86_64-linux.nix @@ -0,0 +1,44 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.hardware.x86_64.common; +in +{ + options.ghaf.hardware.x86_64.common = { + enable = lib.mkEnableOption "Common x86 configs"; + }; + + config = lib.mkIf cfg.enable { + nixpkgs.hostPlatform.system = "x86_64-linux"; + + # Increase the support for different devices by allowing the use + # of proprietary drivers from the respective vendors + nixpkgs.config.allowUnfree = true; + + # Add this for x86_64 hosts to be able to more generically support hardware. + # For example Intel NUC 11's graphics card needs this in order to be able to + # properly provide acceleration. + hardware.enableRedistributableFirmware = true; + hardware.enableAllFirmware = true; + + boot = { + # Enable normal Linux console on the display + kernelParams = [ "console=tty0" ]; + + # To enable installation of ghaf into NVMe drives + initrd.availableKernelModules = [ + "nvme" + "uas" + ]; + loader = { + efi.canTouchEfiVariables = true; + systemd-boot.enable = true; + }; + # ZFS-compatible kernel is used for every applicable target since for certain + # targets ZFS support is required, and having the same kernel version for + # different targets simplifies and hardens the resulting configuration. + kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages; + }; + }; +} diff --git a/modules/host/default.nix b/modules/host/default.nix index 802a37a08..5d2413b27 100644 --- a/modules/host/default.nix +++ b/modules/host/default.nix @@ -3,13 +3,16 @@ # # Modules that should be only imported to host # -{lib, ...}: { +{ lib, ... }: +{ networking.hostName = lib.mkDefault "ghaf-host"; # Overlays should be only defined for host, because microvm.nix uses the # pkgs that already has overlays in place. Otherwise the overlay will be # applied twice. - nixpkgs.overlays = [ - (import ../../overlays/custom-packages) + nixpkgs.overlays = [ (import ../../overlays/custom-packages) ]; + imports = [ + # To push logs to central location + ../common/logging/client.nix ]; } diff --git a/modules/imx8/default.nix b/modules/imx8/default.nix new file mode 100644 index 000000000..bfa01f956 --- /dev/null +++ b/modules/imx8/default.nix @@ -0,0 +1,6 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# Support for Microchip Polarfire Icicle-Kit +# +{ imports = [ ./imx8mp-sdimage.nix ]; } diff --git a/modules/imx8/imx8mp-sdimage.nix b/modules/imx8/imx8mp-sdimage.nix new file mode 100644 index 000000000..c1977c446 --- /dev/null +++ b/modules/imx8/imx8mp-sdimage.nix @@ -0,0 +1,55 @@ +# SPDX-FileCopyrightText: 2023-2024 TII (SSRC) and the Ghaf contributors +# +# SPDX-License-Identifier: Apache-2.0 +{ + config, + pkgs, + modulesPath, + ... +}: +{ + imports = [ (modulesPath + "/installer/sd-card/sd-image.nix") ]; + + disabledModules = [ (modulesPath + "/profiles/all-hardware.nix") ]; + sdImage = { + compressImage = false; + + populateFirmwareCommands = '' + cp ${pkgs.imx8m-boot}/image/flash.bin firmware/ + ''; + + populateRootCommands = '' + mkdir -p ./files/boot + ${config.boot.loader.generic-extlinux-compatible.populateCmd} -c ${config.system.build.toplevel} -d ./files/boot + ''; + + postBuildCommands = '' + sdimage="$out/nixos.img" + fwoffset=64 + blocksize=512 + fwsize=20400 + rootoffset=20800 + + sfdisk --list $img | grep Linux + rootstart=$(sfdisk --list $img | grep Linux | awk '{print $3}') + rootsize=$(sfdisk --list $img | grep Linux | awk '{print $5}') + imagesize=$(((rootoffset + rootsize)*blocksize)) + touch $sdimage + truncate -s $imagesize $sdimage + echo -e " + label: dos + label-id: 0x2178694e + unit: sectors + sector-size: 512 + + start=$fwoffset, size=$fwsize, type=60 + start=$rootoffset, size=$rootsize, type=83, bootable" > "$out/partition.txt" + sfdisk -d $img + sfdisk $sdimage < "$out/partition.txt" + dd conv=notrunc if=${pkgs.imx8m-boot}/image/flash.bin of=$sdimage seek=$fwoffset + dd conv=notrunc if=$img of=$sdimage seek=$rootoffset skip=$rootstart count=$rootsize + sfdisk --list $sdimage + rm -rf $out/sd-image + ''; + }; +} diff --git a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix index 33a409c88..94e795d8b 100644 --- a/modules/jetpack-microvm/agx-gpiovm-passthrough.nix +++ b/modules/jetpack-microvm/agx-gpiovm-passthrough.nix @@ -56,13 +56,5 @@ in { */ } ]; - - /* tmp note: further kernel settings for nvidia in: - ../jetpack/nvidia-jetson-orin/virtualization/default.nix - ../jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix - ../jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix - ../jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix - ../jetpack/nvidia-jetson-orin/virtualization/host/bpmp-virt-host/default.nix - */ }; } diff --git a/modules/jetpack-microvm/agx-netvm-wlan-pci-passthrough.nix b/modules/jetpack-microvm/agx-netvm-wlan-pci-passthrough.nix index 6c2ff08bc..c34dc858b 100644 --- a/modules/jetpack-microvm/agx-netvm-wlan-pci-passthrough.nix +++ b/modules/jetpack-microvm/agx-netvm-wlan-pci-passthrough.nix @@ -1,15 +1,11 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - lib, - config, - ... -}: let +{ lib, config, ... }: +let cfg = config.ghaf.hardware.nvidia.orin.agx; -in { - options.ghaf.hardware.nvidia.orin.agx.enableNetvmWlanPCIPassthrough = - lib.mkEnableOption - "WLAN card PCI passthrough to NetVM"; +in +{ + options.ghaf.hardware.nvidia.orin.agx.enableNetvmWlanPCIPassthrough = lib.mkEnableOption "WLAN card PCI passthrough to NetVM"; config = lib.mkIf cfg.enableNetvmWlanPCIPassthrough { # Orin AGX WLAN card PCI passthrough ghaf.hardware.nvidia.orin.enablePCIPassthroughCommon = true; diff --git a/modules/jetpack-microvm/nx-netvm-ethernet-pci-passthrough.nix b/modules/jetpack-microvm/nx-netvm-ethernet-pci-passthrough.nix index 5ff7e906a..6869654ab 100644 --- a/modules/jetpack-microvm/nx-netvm-ethernet-pci-passthrough.nix +++ b/modules/jetpack-microvm/nx-netvm-ethernet-pci-passthrough.nix @@ -1,15 +1,11 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - lib, - config, - ... -}: let +{ lib, config, ... }: +let cfg = config.ghaf.hardware.nvidia.orin.nx; -in { - options.ghaf.hardware.nvidia.orin.nx.enableNetvmEthernetPCIPassthrough = - lib.mkEnableOption - "Ethernet card PCI passthrough to NetVM"; +in +{ + options.ghaf.hardware.nvidia.orin.nx.enableNetvmEthernetPCIPassthrough = lib.mkEnableOption "Ethernet card PCI passthrough to NetVM"; config = lib.mkIf cfg.enableNetvmEthernetPCIPassthrough { # Orin NX Ethernet card PCI Passthrough ghaf.hardware.nvidia.orin.enablePCIPassthroughCommon = true; diff --git a/modules/jetpack/nvidia-jetson-orin/format-module.nix b/modules/jetpack/nvidia-jetson-orin/format-module.nix index 182dd68fd..ca707decb 100644 --- a/modules/jetpack/nvidia-jetson-orin/format-module.nix +++ b/modules/jetpack/nvidia-jetson-orin/format-module.nix @@ -5,9 +5,7 @@ # nixos-generators flake input as an argument. # { - imports = [ - ./sdimage.nix - ]; + imports = [ ./sdimage.nix ]; formatAttr = "sdImage"; } diff --git a/modules/jetpack/nvidia-jetson-orin/jetson-orin.nix b/modules/jetpack/nvidia-jetson-orin/jetson-orin.nix index 98cf604a1..b378a15af 100644 --- a/modules/jetpack/nvidia-jetson-orin/jetson-orin.nix +++ b/modules/jetpack/nvidia-jetson-orin/jetson-orin.nix @@ -2,77 +2,85 @@ # SPDX-License-Identifier: Apache-2.0 # # Configuration for NVIDIA Jetson Orin AGX/NX reference boards -{ - lib, - config, - ... -}: let +{ lib, config, ... }: +let cfg = config.ghaf.hardware.nvidia.orin; + inherit (lib) + mkEnableOption + mkOption + mkIf + types + ; in - with lib; { - options.ghaf.hardware.nvidia.orin = { - # Enable the Orin boards - enable = mkEnableOption "Orin hardware"; +{ + options.ghaf.hardware.nvidia.orin = { + # Enable the Orin boards + enable = mkEnableOption "Orin hardware"; - flashScriptOverrides.onlyQSPI = - mkEnableOption - "to only flash QSPI partitions, i.e. disable flashing of boot and root partitions to eMMC"; + flashScriptOverrides.onlyQSPI = mkEnableOption "to only flash QSPI partitions, i.e. disable flashing of boot and root partitions to eMMC"; - flashScriptOverrides.preFlashCommands = mkOption { - description = "Commands to run before the actual flashing"; - type = types.str; - default = ""; - }; + flashScriptOverrides.preFlashCommands = mkOption { + description = "Commands to run before the actual flashing"; + type = types.str; + default = ""; + }; - somType = mkOption { - description = "SoM config Type (NX|AGX|Nano)"; - type = types.str; - default = "agx"; - }; + somType = mkOption { + description = "SoM config Type (NX|AGX|Nano)"; + type = types.str; + default = "agx"; + }; - carrierBoard = mkOption { - description = "Board Type"; - type = types.str; - default = "devkit"; - }; + carrierBoard = mkOption { + description = "Board Type"; + type = types.str; + default = "devkit"; }; + }; - config = mkIf cfg.enable { - hardware.nvidia-jetpack = { - enable = true; - som = "orin-${cfg.somType}"; - carrierBoard = "${cfg.carrierBoard}"; - modesetting.enable = true; + config = mkIf cfg.enable { + hardware.nvidia-jetpack = { + enable = true; + som = "orin-${cfg.somType}"; + carrierBoard = "${cfg.carrierBoard}"; + modesetting.enable = true; - flashScriptOverrides = lib.optionalAttrs (cfg.somType == "agx") { - flashArgs = lib.mkForce ["-r" config.hardware.nvidia-jetpack.flashScriptOverrides.targetBoard "mmcblk0p1"]; - }; + flashScriptOverrides = lib.optionalAttrs (cfg.somType == "agx") { + flashArgs = lib.mkForce [ + "-r" + config.hardware.nvidia-jetpack.flashScriptOverrides.targetBoard + "mmcblk0p1" + ]; + }; - firmware.uefi = { - logo = ../../../docs/src/img/1600px-Ghaf_logo.svg; - edk2NvidiaPatches = [ - # This effectively disables EFI FB Simple Framebuffer, which does - # not work properly but causes kernel panic during the boot if the - # HDMI cable is connected during boot time. - # - # The patch reverts back to old behavior, which is to always reset - # the display when exiting UEFI, instead of doing handoff, when - # means not to reset anything. - ./edk2-nvidia-always-reset-display.patch - ]; - }; + firmware.uefi = { + logo = ../../../docs/src/img/1600px-Ghaf_logo.svg; + edk2NvidiaPatches = [ + # This effectively disables EFI FB Simple Framebuffer, which does + # not work properly but causes kernel panic during the boot if the + # HDMI cable is connected during boot time. + # + # The patch reverts back to old behavior, which is to always reset + # the display when exiting UEFI, instead of doing handoff, when + # means not to reset anything. + ./edk2-nvidia-always-reset-display.patch + ]; }; + }; - nixpkgs.hostPlatform.system = "aarch64-linux"; + nixpkgs.hostPlatform.system = "aarch64-linux"; - ghaf.boot.loader.systemd-boot-dtb.enable = true; + ghaf.boot.loader.systemd-boot-dtb.enable = true; - boot.loader = { + boot = { + loader = { efi.canTouchEfiVariables = true; systemd-boot.enable = true; }; - boot.modprobeConfig.enable = true; - boot.kernelPatches = [ + + modprobeConfig.enable = true; + + kernelPatches = [ { name = "vsock-config"; patch = null; @@ -88,27 +96,28 @@ in }; } ]; + }; - services.nvpmodel = { + services.nvpmodel = { + enable = lib.mkDefault true; + # Enable all CPU cores, full power consumption (50W on AGX, 25W on NX) + profileNumber = lib.mkDefault 3; + }; + hardware.deviceTree = + { enable = lib.mkDefault true; - # Enable all CPU cores, full power consumption (50W on AGX, 25W on NX) - profileNumber = lib.mkDefault 3; + # Add the include paths to build the dtb overlays + dtboBuildExtraIncludePaths = [ + "${lib.getDev config.hardware.deviceTree.kernelPackage}/lib/modules/${config.hardware.deviceTree.kernelPackage.modDirVersion}/source/nvidia/soc/t23x/kernel-include" + ]; + } + # Versions of the device tree without PCI passthrough related + # modifications. + // lib.optionalAttrs (cfg.somType == "agx") { + name = lib.mkDefault "tegra234-p3701-0000-p3737-0000.dtb"; + } + // lib.optionalAttrs (cfg.somType == "nx") { + name = lib.mkDefault "tegra234-p3767-0000-p3509-a02.dtb"; }; - hardware.deviceTree = - { - enable = lib.mkDefault true; - # Add the include paths to build the dtb overlays - dtboBuildExtraIncludePaths = [ - "${lib.getDev config.hardware.deviceTree.kernelPackage}/lib/modules/${config.hardware.deviceTree.kernelPackage.modDirVersion}/source/nvidia/soc/t23x/kernel-include" - ]; - } - # Versions of the device tree without PCI passthrough related - # modifications. - // lib.optionalAttrs (cfg.somType == "agx") { - name = lib.mkDefault "tegra234-p3701-0000-p3737-0000.dtb"; - } - // lib.optionalAttrs (cfg.somType == "nx") { - name = lib.mkDefault "tegra234-p3767-0000-p3509-a02.dtb"; - }; - }; - } + }; +} diff --git a/modules/jetpack/nvidia-jetson-orin/mk-esp-contents.py b/modules/jetpack/nvidia-jetson-orin/mk-esp-contents.py index f6e9cd6d1..0f6077efb 100755 --- a/modules/jetpack/nvidia-jetson-orin/mk-esp-contents.py +++ b/modules/jetpack/nvidia-jetson-orin/mk-esp-contents.py @@ -11,13 +11,14 @@ * Use bootspec JSON as primary source of truth about system * Be enough close to standard bootctl behavior, to allow local system updates """ + import argparse import errno import json import os import shutil import sys -from typing import List, Optional, TypedDict +from typing import TypedDict BOOT_ENTRY = """title {title} version Generation {generation} {description} @@ -41,7 +42,7 @@ class BootSpec(TypedDict): kernel: str initrd: str init: str - kernelParams: List[str] + kernelParams: list[str] system: str label: str @@ -85,7 +86,7 @@ def copy_file(src: str, dst: str) -> None: def make_efi_name(store_file_path: str, root: str = "/") -> str: suffix = os.path.basename(store_file_path) store_dir = os.path.basename(os.path.dirname(store_file_path)) - return os.path.join(root, "EFI/nixos/%s-%s.efi" % (store_dir, suffix)) + return os.path.join(root, f"EFI/nixos/{store_dir}-{suffix}.efi") def copy_loader(loader: str, esp: str, target_name: str) -> None: @@ -94,7 +95,7 @@ def copy_loader(loader: str, esp: str, target_name: str) -> None: copy_file(loader, os.path.join(efi, "BOOT", target_name)) -def copy_nixos(esp: str, kernel: str, initrd: str, dtb: Optional[str] = None) -> None: +def copy_nixos(esp: str, kernel: str, initrd: str, dtb: str | None = None) -> None: copy_file(kernel, make_efi_name(kernel, esp)) copy_file(initrd, make_efi_name(initrd, esp)) if dtb: @@ -104,10 +105,10 @@ def copy_nixos(esp: str, kernel: str, initrd: str, dtb: Optional[str] = None) -> def write_loader_entry( esp: str, boot: BootSpec, - kernel_params: List[str], - machine_id: Optional[str], - random_seed: Optional[str], - device_tree: Optional[str], + kernel_params: list[str], + machine_id: str | None, + random_seed: str | None, + device_tree: str | None, ) -> None: entry = BOOT_ENTRY.format( kernel=make_efi_name(boot["kernel"]), @@ -135,9 +136,9 @@ def write_loader_entry( def read_bootspec_file(toplevel: str) -> BootSpec: bootfile = os.path.join(toplevel, "boot.json") ensure_file(bootfile) - with open(bootfile, "r") as boot_json: + with open(bootfile) as boot_json: content = json.load(boot_json) - bootspec: Optional[BootSpec] = content.get("org.nixos.bootspec.v1") + bootspec: BootSpec | None = content.get("org.nixos.bootspec.v1") if bootspec is None: eprint(f"""Can't find "org.nixos.bootspec.v1" in {bootfile}""") sys.exit(1) @@ -147,9 +148,9 @@ def read_bootspec_file(toplevel: str) -> BootSpec: def create_esp_contents( toplevel: str, output: str, - machine_id: Optional[str], - random_seed: Optional[str], - device_tree: Optional[str], + machine_id: str | None, + random_seed: str | None, + device_tree: str | None, ) -> None: mkdir_p(output) boot = read_bootspec_file(toplevel) diff --git a/modules/jetpack/nvidia-jetson-orin/optee.nix b/modules/jetpack/nvidia-jetson-orin/optee.nix index b967a7fc7..1b9f6b8ac 100644 --- a/modules/jetpack/nvidia-jetson-orin/optee.nix +++ b/modules/jetpack/nvidia-jetson-orin/optee.nix @@ -1,6 +1,7 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{lib, ...}: { +{ lib, ... }: +{ options.ghaf.hardware.nvidia.orin.optee = { xtest = lib.mkOption { type = lib.types.bool; diff --git a/modules/jetpack/nvidia-jetson-orin/ota-utils-fix.nix b/modules/jetpack/nvidia-jetson-orin/ota-utils-fix.nix index 847e910be..5c2667eb3 100644 --- a/modules/jetpack/nvidia-jetson-orin/ota-utils-fix.nix +++ b/modules/jetpack/nvidia-jetson-orin/ota-utils-fix.nix @@ -6,28 +6,25 @@ # There is upstream PR waiting for review: # https://github.com/anduril/jetpack-nixos/pull/162 # +{ pkgs, lib, ... }: { - pkgs, - lib, - ... -}: { # mkAfter needed here so that we can be sure the overlay is after the overlay # included from jetpack-nixos. Otherwise it will just override the whole # nvidia-jetpack set. nixpkgs.overlays = lib.mkAfter [ (_final: prev: { - nvidia-jetpack = - prev.nvidia-jetpack - // { - otaUtils = prev.nvidia-jetpack.otaUtils.overrideAttrs (_finalAttrs: prevAttrs: { - depsBuildHost = [pkgs.bash]; + nvidia-jetpack = prev.nvidia-jetpack // { + otaUtils = prev.nvidia-jetpack.otaUtils.overrideAttrs ( + _finalAttrs: prevAttrs: { + depsBuildHost = [ pkgs.bash ]; installPhase = prevAttrs.installPhase + '' substituteInPlace $out/bin/* --replace '#!/usr/bin/env bash' '#!${pkgs.bash}/bin/bash' ''; - }); - }; + } + ); + }; }) ]; } diff --git a/modules/jetpack/nvidia-jetson-orin/partition-template.nix b/modules/jetpack/nvidia-jetson-orin/partition-template.nix index fd5b0eade..fda796b80 100644 --- a/modules/jetpack/nvidia-jetson-orin/partition-template.nix +++ b/modules/jetpack/nvidia-jetson-orin/partition-template.nix @@ -8,7 +8,8 @@ config, lib, ... -}: let +}: +let # Using the same config for all orin boards (for now) # TODO should this be changed when NX added cfg = config.ghaf.hardware.nvidia.orin; @@ -75,18 +76,20 @@ # NVIDIA-supplied flash_t234_qspi_sdmmc.xml, with the partitions specified in # the above partitionsEmmc variable. partitionTemplateReplaceRange = - if !cfg.flashScriptOverrides.onlyQSPI - then { - firstLineCount = 588; - lastLineCount = 2; - } - else { - # If we don't flash anything to eMMC, then we don't need to have the - # XML-tag at all. - firstLineCount = 587; - lastLineCount = 1; - }; - partitionTemplate = pkgs.runCommand "flash.xml" {} ('' + if !cfg.flashScriptOverrides.onlyQSPI then + { + firstLineCount = 588; + lastLineCount = 2; + } + else + { + # If we don't flash anything to eMMC, then we don't need to have the + # XML-tag at all. + firstLineCount = 587; + lastLineCount = 1; + }; + partitionTemplate = pkgs.runCommand "flash.xml" { } ( + '' head -n ${builtins.toString partitionTemplateReplaceRange.firstLineCount} ${pkgs.nvidia-jetpack.bspSrc}/bootloader/t186ref/cfg/flash_t234_qspi_sdmmc.xml >"$out" '' @@ -99,64 +102,65 @@ + '' tail -n ${builtins.toString partitionTemplateReplaceRange.lastLineCount} ${pkgs.nvidia-jetpack.bspSrc}/bootloader/t186ref/cfg/flash_t234_qspi_sdmmc.xml >>"$out" - ''); + '' + ); in - with lib; { - config = mkIf cfg.enable { - hardware.nvidia-jetpack.flashScriptOverrides.partitionTemplate = partitionTemplate; +{ + config = lib.mkIf cfg.enable { + hardware.nvidia-jetpack.flashScriptOverrides.partitionTemplate = partitionTemplate; - ghaf.hardware.nvidia.orin.flashScriptOverrides.preFlashCommands = - '' - echo "============================================================" - echo "ghaf flashing script" - echo "============================================================" - echo "ghaf version: ${config.ghaf.version}" - echo "cross-compiled build: @isCross@" - echo "l4tVersion: @l4tVersion@" - echo "som: ${config.hardware.nvidia-jetpack.som}" - echo "carrierBoard: ${config.hardware.nvidia-jetpack.carrierBoard}" - echo "============================================================" - echo "" - echo "Working dir: $WORKDIR" - echo "Removing bootlodaer/esp.img if it exists ..." - rm -fv "$WORKDIR/bootloader/esp.img" - mkdir -pv "$WORKDIR/bootloader" + ghaf.hardware.nvidia.orin.flashScriptOverrides.preFlashCommands = + '' + echo "============================================================" + echo "ghaf flashing script" + echo "============================================================" + echo "ghaf version: ${config.ghaf.version}" + echo "cross-compiled build: @isCross@" + echo "l4tVersion: @l4tVersion@" + echo "som: ${config.hardware.nvidia-jetpack.som}" + echo "carrierBoard: ${config.hardware.nvidia-jetpack.carrierBoard}" + echo "============================================================" + echo "" + echo "Working dir: $WORKDIR" + echo "Removing bootlodaer/esp.img if it exists ..." + rm -fv "$WORKDIR/bootloader/esp.img" + mkdir -pv "$WORKDIR/bootloader" - # See https://developer.download.nvidia.com/embedded/L4T/r35_Release_v4.1/docs/Jetson_Linux_Release_Notes_r35.4.1.pdf - # and https://developer.download.nvidia.com/embedded/L4T/r35_Release_v5.0/docs/Jetson_Linux_Release_Notes_r35.5.0.pdf - # - # In Section: Adaptation to the Carrier Board with HDMI for the Orin - # NX/Nano Modules - @patch@ -p0 < ${./tegra2-mb2-bct-scr.patch} - '' - + lib.optionalString (!cfg.flashScriptOverrides.onlyQSPI) '' - ESP_OFFSET=$(cat "${images}/esp.offset") - ESP_SIZE=$(cat "${images}/esp.size") - ROOT_OFFSET=$(cat "${images}/root.offset") - ROOT_SIZE=$(cat "${images}/root.size") + # See https://developer.download.nvidia.com/embedded/L4T/r35_Release_v4.1/docs/Jetson_Linux_Release_Notes_r35.4.1.pdf + # and https://developer.download.nvidia.com/embedded/L4T/r35_Release_v5.0/docs/Jetson_Linux_Release_Notes_r35.5.0.pdf + # + # In Section: Adaptation to the Carrier Board with HDMI for the Orin + # NX/Nano Modules + @patch@ -p0 < ${./tegra2-mb2-bct-scr.patch} + '' + + lib.optionalString (!cfg.flashScriptOverrides.onlyQSPI) '' + ESP_OFFSET=$(cat "${images}/esp.offset") + ESP_SIZE=$(cat "${images}/esp.size") + ROOT_OFFSET=$(cat "${images}/root.offset") + ROOT_SIZE=$(cat "${images}/root.size") - img="${images}/sd-image/${config.sdImage.imageName}.zst" - echo "Extracting ESP partition to $WORKDIR/bootloader/esp.img ..." - dd if=<(@pzstd@ -d "$img" -c) of="$WORKDIR/bootloader/esp.img" bs=512 iseek="$ESP_OFFSET" count="$ESP_SIZE" - echo "Extracting root partition to $WORKDIR/root.img ..." - dd if=<(@pzstd@ -d "$img" -c) of="$WORKDIR/bootloader/root.img" bs=512 iseek="$ROOT_OFFSET" count="$ROOT_SIZE" + img="${images}/sd-image/${config.sdImage.imageName}.zst" + echo "Extracting ESP partition to $WORKDIR/bootloader/esp.img ..." + dd if=<(@pzstd@ -d "$img" -c) of="$WORKDIR/bootloader/esp.img" bs=512 iseek="$ESP_OFFSET" count="$ESP_SIZE" + echo "Extracting root partition to $WORKDIR/root.img ..." + dd if=<(@pzstd@ -d "$img" -c) of="$WORKDIR/bootloader/root.img" bs=512 iseek="$ROOT_OFFSET" count="$ROOT_SIZE" - echo "Patching flash.xml with absolute paths to esp.img and root.img ..." - @sed@ -i \ - -e "s#bootloader/esp.img#$WORKDIR/bootloader/esp.img#" \ - -e "s#root.img#$WORKDIR/root.img#" \ - -e "s#ESP_SIZE#$((ESP_SIZE * 512))#" \ - -e "s#ROOT_SIZE#$((ROOT_SIZE * 512))#" \ - flash.xml + echo "Patching flash.xml with absolute paths to esp.img and root.img ..." + @sed@ -i \ + -e "s#bootloader/esp.img#$WORKDIR/bootloader/esp.img#" \ + -e "s#root.img#$WORKDIR/root.img#" \ + -e "s#ESP_SIZE#$((ESP_SIZE * 512))#" \ + -e "s#ROOT_SIZE#$((ROOT_SIZE * 512))#" \ + flash.xml - '' - + lib.optionalString cfg.flashScriptOverrides.onlyQSPI '' - echo "Flashing QSPI only, boot and root images not included." - '' - + '' - echo "Ready to flash!" - echo "============================================================" - echo "" - ''; - }; - } + '' + + lib.optionalString cfg.flashScriptOverrides.onlyQSPI '' + echo "Flashing QSPI only, boot and root images not included." + '' + + '' + echo "Ready to flash!" + echo "============================================================" + echo "" + ''; + }; +} diff --git a/modules/jetpack/nvidia-jetson-orin/pci-passthrough-common.nix b/modules/jetpack/nvidia-jetson-orin/pci-passthrough-common.nix index 49791462a..56d447427 100644 --- a/modules/jetpack/nvidia-jetson-orin/pci-passthrough-common.nix +++ b/modules/jetpack/nvidia-jetson-orin/pci-passthrough-common.nix @@ -1,15 +1,11 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - lib, - config, - ... -}: let +{ lib, config, ... }: +let cfg = config.ghaf.hardware.nvidia.orin; -in { - options.ghaf.hardware.nvidia.orin.enablePCIPassthroughCommon = - lib.mkEnableOption - "Enable common options related to PCI passthrough on Orin AGX and NX"; +in +{ + options.ghaf.hardware.nvidia.orin.enablePCIPassthroughCommon = lib.mkEnableOption "Enable common options related to PCI passthrough on Orin AGX and NX"; config = lib.mkIf cfg.enablePCIPassthroughCommon { boot.kernelModules = [ "vfio_pci" diff --git a/modules/jetpack/nvidia-jetson-orin/sdimage.nix b/modules/jetpack/nvidia-jetson-orin/sdimage.nix index 6bb8ba69d..870d834fa 100644 --- a/modules/jetpack/nvidia-jetson-orin/sdimage.nix +++ b/modules/jetpack/nvidia-jetson-orin/sdimage.nix @@ -16,66 +16,71 @@ pkgs, modulesPath, ... -}: { - imports = [ - (modulesPath + "/installer/sd-card/sd-image.nix") - ]; +}: +{ + imports = [ (modulesPath + "/installer/sd-card/sd-image.nix") ]; boot.loader.grub.enable = false; - disabledModules = [(modulesPath + "/profiles/all-hardware.nix")]; + disabledModules = [ (modulesPath + "/profiles/all-hardware.nix") ]; - sdImage = let - mkESPContentSource = pkgs.substituteAll { - src = ./mk-esp-contents.py; - isExecutable = true; - inherit (pkgs.buildPackages) python3; - }; - mkESPContent = - pkgs.runCommand "mk-esp-contents" { - nativeBuildInputs = with pkgs; [mypy python3]; - } '' - install -m755 ${mkESPContentSource} $out - mypy \ - --no-implicit-optional \ - --disallow-untyped-calls \ - --disallow-untyped-defs \ - $out + sdImage = + let + mkESPContentSource = pkgs.substituteAll { + src = ./mk-esp-contents.py; + isExecutable = true; + inherit (pkgs.buildPackages) python3; + }; + mkESPContent = + pkgs.runCommand "mk-esp-contents" + { + nativeBuildInputs = with pkgs; [ + mypy + python3 + ]; + } + '' + install -m755 ${mkESPContentSource} $out + mypy \ + --no-implicit-optional \ + --disallow-untyped-calls \ + --disallow-untyped-defs \ + $out + ''; + fdtPath = "${config.hardware.deviceTree.package}/${config.hardware.deviceTree.name}"; + in + { + firmwareSize = 256; + populateFirmwareCommands = '' + mkdir -pv firmware + ${mkESPContent} \ + --toplevel ${config.system.build.toplevel} \ + --output firmware/ \ + --device-tree ${fdtPath} ''; - fdtPath = "${config.hardware.deviceTree.package}/${config.hardware.deviceTree.name}"; - in { - firmwareSize = 256; - populateFirmwareCommands = '' - mkdir -pv firmware - ${mkESPContent} \ - --toplevel ${config.system.build.toplevel} \ - --output firmware/ \ - --device-tree ${fdtPath} - ''; - populateRootCommands = '' - ''; - postBuildCommands = '' - img=$out/sd-image/${config.sdImage.imageName} - fdisk_output=$(fdisk -l "$img") + populateRootCommands = ''''; + postBuildCommands = '' + img=$out/sd-image/${config.sdImage.imageName} + fdisk_output=$(fdisk -l "$img") - # Offsets and sizes are in 512 byte sectors - blocksize=512 + # Offsets and sizes are in 512 byte sectors + blocksize=512 - # ESP partition offset and sector count - part_esp=$(echo -n "$fdisk_output" | tail -n 2 | head -n 1 | tr -s ' ') - part_esp_begin=$(echo -n "$part_esp" | cut -d ' ' -f2) - part_esp_count=$(echo -n "$part_esp" | cut -d ' ' -f4) + # ESP partition offset and sector count + part_esp=$(echo -n "$fdisk_output" | tail -n 2 | head -n 1 | tr -s ' ') + part_esp_begin=$(echo -n "$part_esp" | cut -d ' ' -f2) + part_esp_count=$(echo -n "$part_esp" | cut -d ' ' -f4) - # root-partition offset and sector count - part_root=$(echo -n "$fdisk_output" | tail -n 1 | head -n 1 | tr -s ' ') - part_root_begin=$(echo -n "$part_root" | cut -d ' ' -f3) - part_root_count=$(echo -n "$part_root" | cut -d ' ' -f4) + # root-partition offset and sector count + part_root=$(echo -n "$fdisk_output" | tail -n 1 | head -n 1 | tr -s ' ') + part_root_begin=$(echo -n "$part_root" | cut -d ' ' -f3) + part_root_count=$(echo -n "$part_root" | cut -d ' ' -f4) - echo -n $part_esp_begin > $out/esp.offset - echo -n $part_esp_count > $out/esp.size - echo -n $part_root_begin > $out/root.offset - echo -n $part_root_count > $out/root.size - ''; - }; + echo -n $part_esp_begin > $out/esp.offset + echo -n $part_esp_count > $out/esp.size + echo -n $part_root_begin > $out/root.offset + echo -n $part_root_count > $out/root.size + ''; + }; fileSystems."/boot" = { device = "/dev/disk/by-label/${config.sdImage.firmwarePartitionName}"; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix index 8af097266..3f509de4c 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/bpmp-virt-common/default.nix @@ -1,12 +1,24 @@ # Copyright 2022-2023 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 +{ lib, config, ... }: +let + cfg = config.ghaf.hardware.nvidia.virtualization; +in { - lib, - config, - ... -}: let - cfg = config.ghaf.hardware.nvidia.virtualization.host.bpmp; -in { + /* already declared in ./modules/jetpack/nvidia-jetson-orin/virtualization/ + options.ghaf.hardware.nvidia.virtualization.enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Enable virtualization support for NVIDIA Orin + + This option is an implementation level detail and is toggled automatically + by modules that need it. Manually enabling this option is not recommended in + release builds. + ''; + }; + */ + config = lib.mkIf cfg.enable { boot.kernelPatches = [ /* configure kernel in modules/hardware/nvidia-jetson-orin/virtualization/default.nix for all virtualisation @@ -52,6 +64,6 @@ in { } ]; - boot.kernelParams = ["vfio_iommu_type1.allow_unsafe_interrupts=1"]; + boot.kernelParams = [ "vfio_iommu_type1.allow_unsafe_interrupts=1" ]; }; } diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix index a9bf5bf17..744e41b99 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/default.nix @@ -8,31 +8,7 @@ cfg = config.ghaf.hardware.nvidia.virtualization.host.gpio; in { config = lib.mkIf cfg.enable { - boot.kernelPatches = [ - /* configure kernel in modules/hardware/nvidia-jetson-orin/virtualization/default.nix for all virtualisation - * TODO: differentiate config - { - name = "Added Configurations to Support GPIO passthrough"; - patch = null; - extraStructuredConfig = { - PCI_STUB = lib.mkDefault lib.kernel.yes; - HOTPLUG_PCI = lib.mkDefault lib.kernel.yes; - HOTPLUG_PCI_ACPI = lib.mkDefault lib.kernel.yes; - PCI_DEBUG = lib.mkDefault lib.kernel.yes; - PCI_HOST_GENERIC = lib.mkDefault lib.kernel.yes; - PCI_HOST_COMMON = lib.mkDefault lib.kernel.yes; - VFIO = lib.mkDefault lib.kernel.yes; - VFIO_IOMMU_TYPE1 = lib.mkDefault lib.kernel.yes; - VFIO_PLATFORM = lib.mkDefault lib.kernel.yes; - VIRTIO_PCI = lib.mkDefault lib.kernel.yes; - VIRTIO_MMIO = lib.mkDefault lib.kernel.yes; - CONFIG_GPIO_TEGRA = lib.mkDefault lib.kernel.yes; - CONFIG_GPIO_TEGRA186 = lib.mkDefault lib.kernel.yes; - TEGRA_GPIO_GUEST_PROXY = lib.mkDefault lib.kernel.yes; - TEGRA_GPIO_HOST_PROXY = lib.mkDefault lib.kernel.yes; - }; - } - */ + boot.kernelPatches = builtins.trace "Patching kernel for GPIO passthrough" [ /* This patch is not needed because of the kernel parameters { name = "Vfio_platform Reset Required False"; @@ -62,12 +38,13 @@ in { } */ ]; - + /* boot.kernelParams = [ "iommu=pt" "vfio.enable_unsafe_noiommu_mode=0" "vfio_iommu_type1.allow_unsafe_interrupts=1" "vfio_platform.reset_required=0" ]; + */ }; } diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch index e5eeb58cf..c51aa75a3 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0003-gpio-virt-kernel.patch @@ -351,7 +351,7 @@ index f66fc17faee4..9706a77fe5a5 100644 } subsys_initcall(tegra_gpio_init); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c -index 5e57824b283e..43f83675ec3f 100644 +index 5e57824b283e..65a9cd915e3e 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -20,6 +20,46 @@ @@ -359,7 +359,7 @@ index 5e57824b283e..43f83675ec3f 100644 #include +#define GPIO_DEBUG -+#define GPIO_DEBUG_VERBOSE ++// #define GPIO_DEBUG_VERBOSE + +#ifdef GPIO_DEBUG + #define deb_info(fmt, ...) printk(KERN_INFO "GPIO func \'%s\' in file \'%s\' -- " fmt, __func__, kbasename(__FILE__), ##__VA_ARGS__) @@ -933,7 +933,7 @@ index 5e57824b283e..43f83675ec3f 100644 } } } -@@ -1107,6 +1235,232 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +@@ -1107,6 +1235,235 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) return -EINVAL; } @@ -1042,6 +1042,9 @@ index 5e57824b283e..43f83675ec3f 100644 +#if defined(CONFIG_TEGRA_GPIO_GUEST_PROXY) || defined(CONFIG_TEGRA_GPIO_HOST_PROXY) + + extern int tegra_gpio_guest_init(void); ++ extern int tegra_gpio_host_init(void); ++ extern int tegra_gpio_guest_cleanup(void); ++ extern int tegra_gpio_host_cleanup(void); + + #define MAX_CHIP 2 // check this value against value in gpio_host-proxy.h + @@ -1166,11 +1169,12 @@ index 5e57824b283e..43f83675ec3f 100644 static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; -@@ -1120,17 +1474,40 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1120,17 +1477,41 @@ static int tegra186_gpio_probe(struct platform_device *pdev) int value; void __iomem *base; + static bool guest_proxy_is_set_up = false; ++ static bool host_proxy_is_set_up = false; + + deb_debug("Probing gpio"); + @@ -1211,7 +1215,7 @@ index 5e57824b283e..43f83675ec3f 100644 /* count the number of banks in the controller */ for (i = 0; i < gpio->soc->num_ports; i++) -@@ -1139,11 +1516,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1139,11 +1520,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->num_banks++; @@ -1228,7 +1232,7 @@ index 5e57824b283e..43f83675ec3f 100644 sizeof(*gpio->gpio_rval), GFP_KERNEL); if (!gpio->gpio_rval) return -ENOMEM; -@@ -1154,9 +1535,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1154,9 +1539,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -EINVAL; } @@ -1241,7 +1245,7 @@ index 5e57824b283e..43f83675ec3f 100644 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gte"); if (!res) { dev_err(&pdev->dev, "Missing gte MEM resource\n"); -@@ -1172,14 +1556,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1172,14 +1560,20 @@ static int tegra186_gpio_probe(struct platform_device *pdev) } err = platform_irq_count(pdev); @@ -1265,7 +1269,7 @@ index 5e57824b283e..43f83675ec3f 100644 gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), GFP_KERNEL); -@@ -1188,27 +1578,43 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1188,27 +1582,48 @@ static int tegra186_gpio_probe(struct platform_device *pdev) for (i = 0; i < gpio->num_irq; i++) { err = platform_get_irq(pdev, i); @@ -1300,7 +1304,7 @@ index 5e57824b283e..43f83675ec3f 100644 + tegra_gte_setup(gpio); + + if(kernel_is_on_guest) { -+ deb_debug("GPIO Guest init section\n"); ++ deb_debug("GPIO Proxy init section\n"); + if( ! guest_proxy_is_set_up ) { + ret = tegra_gpio_guest_init(); + guest_proxy_is_set_up = true; @@ -1309,11 +1313,16 @@ index 5e57824b283e..43f83675ec3f 100644 + gpio_hook(gpio); + } + else { ++ if( ! host_proxy_is_set_up ) { ++ ret = tegra_gpio_host_init(); ++ host_proxy_is_set_up = true; ++ } + // gpio_unhook is the same as standard settings + // unhooked pointers are for the host driver on host only + BUG_ON(gpio_vpa != 0); // assert we do not set up the vpa driver + gpio_unhook(gpio); // set standard function pointers -+ } ++ }; ++ + gpio->gpio.base = -1; + deb_debug("gpio function pointers are set for gpio label=%s\n", gpio->gpio.label); + #else @@ -1325,7 +1334,7 @@ index 5e57824b283e..43f83675ec3f 100644 for (i = 0; i < gpio->soc->num_ports; i++) gpio->gpio.ngpio += gpio->soc->ports[i].pins; -@@ -1229,6 +1635,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1229,6 +1644,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -ENOMEM; names[offset + j] = name; @@ -1333,7 +1342,7 @@ index 5e57824b283e..43f83675ec3f 100644 } offset += port->pins; -@@ -1260,6 +1667,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1260,6 +1676,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parent_handler_data = gpio; irq->num_parents = gpio->num_irq; @@ -1342,7 +1351,7 @@ index 5e57824b283e..43f83675ec3f 100644 /* * To simplify things, use a single interrupt per bank for now. Some -@@ -1284,7 +1693,18 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1284,7 +1702,18 @@ static int tegra186_gpio_probe(struct platform_device *pdev) } if (gpio->soc->num_irqs_per_bank > 1) @@ -1361,7 +1370,7 @@ index 5e57824b283e..43f83675ec3f 100644 np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (!of_device_is_available(np)) -@@ -1315,9 +1735,31 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1315,9 +1744,31 @@ static int tegra186_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, gpio); @@ -1396,7 +1405,7 @@ index 5e57824b283e..43f83675ec3f 100644 if (gpio->soc->is_hw_ts_sup) { for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) { -@@ -1325,28 +1767,29 @@ static int tegra186_gpio_probe(struct platform_device *pdev) +@@ -1325,28 +1776,29 @@ static int tegra186_gpio_probe(struct platform_device *pdev) &gpio->soc->ports[i]; for (j = 0; j < port->pins; j++) { @@ -1434,7 +1443,7 @@ index 5e57824b283e..43f83675ec3f 100644 static int tegra_gpio_resume_early(struct device *dev) { struct tegra_gpio *gpio = dev_get_drvdata(dev); -@@ -1355,7 +1798,7 @@ static int tegra_gpio_resume_early(struct device *dev) +@@ -1355,7 +1807,7 @@ static int tegra_gpio_resume_early(struct device *dev) void __iomem *base; int i; @@ -1443,7 +1452,7 @@ index 5e57824b283e..43f83675ec3f 100644 if (WARN_ON(base == NULL)) return -EINVAL; -@@ -1366,9 +1809,9 @@ static int tegra_gpio_resume_early(struct device *dev) +@@ -1366,9 +1818,9 @@ static int tegra_gpio_resume_early(struct device *dev) regs->restore_needed = false; @@ -1456,7 +1465,20 @@ index 5e57824b283e..43f83675ec3f 100644 } return 0; -@@ -1723,7 +2166,9 @@ static struct platform_driver tegra186_gpio_driver = { +@@ -1392,6 +1844,12 @@ static const struct dev_pm_ops tegra_gpio_pm = { + + static int tegra186_gpio_remove(struct platform_device *pdev) + { ++ if(kernel_is_on_guest) { ++ tegra_gpio_guest_cleanup(); ++ } ++ else { ++ tegra_gpio_host_cleanup(); ++ } + return 0; + } + +@@ -1723,7 +2181,9 @@ static struct platform_driver tegra186_gpio_driver = { .probe = tegra186_gpio_probe, .remove = tegra186_gpio_remove, }; @@ -1781,7 +1803,7 @@ index 30e2476a6dc4..aab40f1cd52f 100644 err = gpio_request_one(array->gpio, array->flags, array->label); if (err) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c -index 647e77db82b1..4b85fcb8b62b 100644 +index f54b5905e2cc..5d9425f0f7fe 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -24,6 +24,26 @@ @@ -1903,7 +1925,7 @@ index 3ef71ca242ba..bf62aeee66a1 100644 /* If buf is not a number then try to find by name */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c -index 50abb1c20df0..cf7296db3190 100644 +index 94b70e0636fe..38de01b3da7d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -31,6 +31,26 @@ @@ -1933,20 +1955,6 @@ index 50abb1c20df0..cf7296db3190 100644 /* Implementation infrastructure for GPIO interfaces. * * The GPIO programming interface allows for inlining speed-critical -@@ -45,11 +65,11 @@ - * - * Otherwise, minimize overhead in what may be bitbanging codepaths. - */ --#ifdef DEBUG -+ #ifdef DEBUG - #define extra_checks 1 - #else - #define extra_checks 0 --#endif -+ #endif - - /* Device and char device-related information */ - static DEFINE_IDA(gpio_ida); @@ -105,6 +125,8 @@ struct gpio_desc *gpio_to_desc(unsigned gpio) { struct gpio_device *gdev; @@ -2000,7 +2008,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (list_empty(&gpio_devices)) { /* initial entry in list */ list_add_tail(&gdev->list, &gpio_devices); -@@ -270,14 +301,12 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) +@@ -270,6 +301,7 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) if (prev->base + prev->ngpio <= gdev->base) { /* add behind last entry */ list_add_tail(&gdev->list, &gpio_devices); @@ -2008,15 +2016,7 @@ index 50abb1c20df0..cf7296db3190 100644 return 0; } - list_for_each_entry_safe(prev, next, &gpio_devices, list) { - /* at the end of the list */ -- if (&next->list == &gpio_devices) -- break; -- - /* add between prev and next */ - if (prev->base + prev->ngpio <= gdev->base - && gdev->base + gdev->ngpio <= next->base) { -@@ -301,6 +330,8 @@ struct gpio_desc *gpio_name_to_desc(const char * const name) +@@ -301,6 +333,8 @@ struct gpio_desc *gpio_name_to_desc(const char * const name) struct gpio_device *gdev; unsigned long flags; @@ -2025,7 +2025,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!name) return NULL; -@@ -340,6 +371,8 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) +@@ -340,6 +374,8 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) struct gpio_device *gdev = gc->gpiodev; int i; @@ -2034,7 +2034,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* First check all names if they are unique */ for (i = 0; i != gc->ngpio; ++i) { struct gpio_desc *gpio; -@@ -375,6 +408,8 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip) +@@ -375,6 +411,8 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip) int ret, i; int count; @@ -2043,7 +2043,7 @@ index 50abb1c20df0..cf7296db3190 100644 count = fwnode_property_string_array_count(fwnode, "gpio-line-names"); if (count < 0) return 0; -@@ -409,6 +444,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) +@@ -409,6 +447,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) { unsigned long *p; @@ -2052,7 +2052,7 @@ index 50abb1c20df0..cf7296db3190 100644 p = bitmap_alloc(gc->ngpio, GFP_KERNEL); if (!p) return NULL; -@@ -421,6 +458,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) +@@ -421,6 +461,8 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc) static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) { @@ -2061,7 +2061,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask)) return 0; -@@ -433,6 +472,8 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) +@@ -433,6 +475,8 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) static int gpiochip_init_valid_mask(struct gpio_chip *gc) { @@ -2070,7 +2070,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (gc->init_valid_mask) return gc->init_valid_mask(gc, gc->valid_mask, -@@ -443,12 +484,16 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) +@@ -443,12 +487,16 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_free_valid_mask(struct gpio_chip *gc) { @@ -2087,7 +2087,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (gc->add_pin_ranges) return gc->add_pin_ranges(gc); -@@ -458,6 +503,8 @@ static int gpiochip_add_pin_ranges(struct gpio_chip *gc) +@@ -458,6 +506,8 @@ static int gpiochip_add_pin_ranges(struct gpio_chip *gc) bool gpiochip_line_is_valid(const struct gpio_chip *gc, unsigned int offset) { @@ -2096,7 +2096,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* No mask means all valid */ if (likely(!gc->valid_mask)) return true; -@@ -470,6 +517,8 @@ static void gpiodevice_release(struct device *dev) +@@ -470,6 +520,8 @@ static void gpiodevice_release(struct device *dev) struct gpio_device *gdev = dev_get_drvdata(dev); unsigned long flags; @@ -2105,7 +2105,7 @@ index 50abb1c20df0..cf7296db3190 100644 spin_lock_irqsave(&gpio_lock, flags); list_del(&gdev->list); spin_unlock_irqrestore(&gpio_lock, flags); -@@ -480,7 +529,7 @@ static void gpiodevice_release(struct device *dev) +@@ -480,7 +532,7 @@ static void gpiodevice_release(struct device *dev) kfree(gdev); } @@ -2114,7 +2114,7 @@ index 50abb1c20df0..cf7296db3190 100644 #define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt)) #define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev)) #else -@@ -490,13 +539,66 @@ static void gpiodevice_release(struct device *dev) +@@ -490,13 +542,27 @@ static void gpiodevice_release(struct device *dev) */ #define gcdev_register(gdev, devt) device_add(&(gdev)->dev) #define gcdev_unregister(gdev) device_del(&(gdev)->dev) @@ -2138,28 +2138,15 @@ index 50abb1c20df0..cf7296db3190 100644 + proxy_host_gpio_dev[gpio_dev_count++] = gdev; + // we continue to populate gdev + -+ ret = gcdev_register(gdev, gpio_devt); -+ -+ if (ret) -+ return ret; -+ -+ ret = gpiochip_sysfs_register(gdev); -+ if (ret) -+ goto err_remove_device; -+ -+ /* From this point, the .release() function cleans up gpio_device */ -+ gdev->dev.release = gpiodevice_release; -+ pr_info("%s: registered GPIOs %d to %d on %s\n", -+ dev_name(&gdev->dev), gdev->base, -+ gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic"); -+ -+ return 0; -+ -+err_remove_device: -+ gcdev_unregister(gdev); -+ return ret; -+} + ret = gcdev_register(gdev, gpio_devt); + + if (ret) + return ret; + +@@ -517,11 +583,55 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) + return ret; + } + +/* redirecting function to allow guest VMto use it even if hardware is not present */ +static int gpiochip_setup_dev__redirect(struct gpio_device *gdev) +{ @@ -2177,22 +2164,33 @@ index 50abb1c20df0..cf7296db3190 100644 +// FIXIT -- debug +//goto debug_end; + - ret = gcdev_register(gdev, gpio_devt); ++ ret = gcdev_register(gdev, gpio_devt); ++ ++ if (ret) ++ return ret; ++ ++ ret = gpiochip_sysfs_register(gdev); ++ if (ret) ++ goto err_remove_device; + - if (ret) - return ret; - -@@ -504,6 +606,9 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) - if (ret) - goto err_remove_device; - +// FIXIT --debug +//debug_end: + - /* From this point, the .release() function cleans up gpio_device */ - gdev->dev.release = gpiodevice_release; - pr_info("%s: registered GPIOs %d to %d on %s\n", -@@ -522,6 +627,8 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) ++ /* From this point, the .release() function cleans up gpio_device */ ++ gdev->dev.release = gpiodevice_release; ++ pr_info("%s: registered GPIOs %d to %d on %s\n", ++ dev_name(&gdev->dev), gdev->base, ++ gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic"); ++ ++ return 0; ++ ++err_remove_device: ++ gcdev_unregister(gdev); ++ return ret; ++} ++ + static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) + { struct gpio_desc *desc; int rv; @@ -2201,7 +2199,7 @@ index 50abb1c20df0..cf7296db3190 100644 desc = gpiochip_get_desc(gc, hog->chip_hwnum); if (IS_ERR(desc)) { chip_err(gc, "%s: unable to get GPIO desc: %ld\n", __func__, -@@ -542,6 +649,8 @@ static void machine_gpiochip_add(struct gpio_chip *gc) +@@ -542,6 +652,8 @@ static void machine_gpiochip_add(struct gpio_chip *gc) { struct gpiod_hog *hog; @@ -2210,7 +2208,7 @@ index 50abb1c20df0..cf7296db3190 100644 mutex_lock(&gpio_machine_hogs_mutex); list_for_each_entry(hog, &gpio_machine_hogs, list) { -@@ -557,6 +666,8 @@ static void gpiochip_setup_devs(void) +@@ -557,6 +669,8 @@ static void gpiochip_setup_devs(void) struct gpio_device *gdev; int ret; @@ -2219,7 +2217,7 @@ index 50abb1c20df0..cf7296db3190 100644 list_for_each_entry(gdev, &gpio_devices, list) { ret = gpiochip_setup_dev(gdev); if (ret) -@@ -576,6 +687,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -576,6 +690,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = gc->base; struct gpio_device *gdev; @@ -2228,7 +2226,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* * First: allocate and populate the internal stat container, and * set up the struct device. -@@ -591,13 +704,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -591,13 +707,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gdev->dev.of_node = gc->parent->of_node; } @@ -2244,7 +2242,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* * Assign fwnode depending on the result of the previous calls, -@@ -689,9 +802,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -689,9 +805,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier); @@ -2256,7 +2254,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (gc->names) ret = gpiochip_set_desc_names(gc); -@@ -793,6 +906,256 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, +@@ -793,6 +909,256 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, } EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); @@ -2513,7 +2511,7 @@ index 50abb1c20df0..cf7296db3190 100644 /** * gpiochip_get_data() - get per-subdriver data for the chip * @gc: GPIO chip -@@ -802,6 +1165,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); +@@ -802,6 +1168,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); */ void *gpiochip_get_data(struct gpio_chip *gc) { @@ -2522,7 +2520,7 @@ index 50abb1c20df0..cf7296db3190 100644 return gc->gpiodev->data; } EXPORT_SYMBOL_GPL(gpiochip_get_data); -@@ -818,6 +1183,8 @@ void gpiochip_remove(struct gpio_chip *gc) +@@ -818,6 +1186,8 @@ void gpiochip_remove(struct gpio_chip *gc) unsigned long flags; unsigned int i; @@ -2531,7 +2529,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ gpiochip_sysfs_unregister(gdev); gpiochip_free_hogs(gc); -@@ -875,10 +1242,13 @@ struct gpio_chip *gpiochip_find(void *data, +@@ -875,10 +1245,13 @@ struct gpio_chip *gpiochip_find(void *data, struct gpio_chip *gc = NULL; unsigned long flags; @@ -2545,7 +2543,7 @@ index 50abb1c20df0..cf7296db3190 100644 break; } -@@ -895,12 +1265,15 @@ static int gpiochip_match_name(struct gpio_chip *gc, void *data) +@@ -895,12 +1268,15 @@ static int gpiochip_match_name(struct gpio_chip *gc, void *data) return !strcmp(gc->label, name); } @@ -2563,7 +2561,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* * The following is irqchip helper code for gpiochips. -@@ -910,6 +1283,8 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc) +@@ -910,6 +1286,8 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc) { struct gpio_irq_chip *girq = &gc->irq; @@ -2572,7 +2570,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!girq->init_hw) return 0; -@@ -920,6 +1295,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -920,6 +1298,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) { struct gpio_irq_chip *girq = &gc->irq; @@ -2581,7 +2579,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!girq->init_valid_mask) return 0; -@@ -934,6 +1311,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -934,6 +1314,8 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) { @@ -2590,7 +2588,7 @@ index 50abb1c20df0..cf7296db3190 100644 bitmap_free(gc->irq.valid_mask); gc->irq.valid_mask = NULL; } -@@ -941,6 +1320,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) +@@ -941,6 +1323,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, unsigned int offset) { @@ -2599,7 +2597,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!gpiochip_line_is_valid(gc, offset)) return false; /* No mask means all valid */ -@@ -966,6 +1347,8 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, +@@ -966,6 +1350,8 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, struct gpio_irq_chip *girq = &gc->irq; struct device *dev = &gc->gpiodev->dev; @@ -2608,7 +2606,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!girq->domain) { chip_err(gc, "called %s before setting up irqchip\n", __func__); -@@ -1011,7 +1394,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gc, +@@ -1011,7 +1397,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gc, } EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); @@ -2617,7 +2615,7 @@ index 50abb1c20df0..cf7296db3190 100644 /** * gpiochip_set_hierarchical_irqchip() - connects a hierarchical irqchip -@@ -1250,6 +1633,8 @@ void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, +@@ -1250,6 +1636,8 @@ void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, { struct irq_fwspec *fwspec; @@ -2626,7 +2624,7 @@ index 50abb1c20df0..cf7296db3190 100644 fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); if (!fwspec) return NULL; -@@ -1269,6 +1654,8 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, +@@ -1269,6 +1657,8 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, { struct irq_fwspec *fwspec; @@ -2635,7 +2633,7 @@ index 50abb1c20df0..cf7296db3190 100644 fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); if (!fwspec) return NULL; -@@ -1296,7 +1683,7 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) +@@ -1296,7 +1686,7 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) return false; } @@ -2644,7 +2642,7 @@ index 50abb1c20df0..cf7296db3190 100644 /** * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip -@@ -1314,6 +1701,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, +@@ -1314,6 +1704,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, struct gpio_chip *gc = d->host_data; int ret = 0; @@ -2653,25 +2651,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!gpiochip_irqchip_irq_valid(gc, hwirq)) return -ENXIO; -@@ -1415,7 +1804,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) - if (!gpiochip_irqchip_irq_valid(gc, offset)) - return -ENXIO; - --#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY -+ #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY - if (irq_domain_is_hierarchy(domain)) { - struct irq_fwspec spec; - -@@ -1426,7 +1815,7 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) - - return irq_create_fwspec_mapping(&spec); - } --#endif -+ #endif - - return irq_create_mapping(domain, offset); - } -@@ -1709,7 +2098,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, +@@ -1728,7 +2120,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, } gc->irq.threaded = threaded; of_node = gc->parent->of_node; @@ -2680,7 +2660,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* * If the gpiochip has an assigned OF node this takes precedence * FIXME: get rid of this and use gc->parent->of_node -@@ -1717,7 +2106,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, +@@ -1736,7 +2128,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gc, */ if (gc->of_node) of_node = gc->of_node; @@ -2689,7 +2669,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* * Specifying a default trigger is a terrible idea if DT or ACPI is * used to configure the interrupts, as you may end-up with -@@ -1796,7 +2185,7 @@ static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) +@@ -1815,7 +2207,7 @@ static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) { } @@ -2698,7 +2678,7 @@ index 50abb1c20df0..cf7296db3190 100644 /** * gpiochip_generic_request() - request the gpio function for a pin -@@ -1805,10 +2194,12 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) +@@ -1824,10 +2216,12 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) */ int gpiochip_generic_request(struct gpio_chip *gc, unsigned offset) { @@ -2713,7 +2693,7 @@ index 50abb1c20df0..cf7296db3190 100644 return pinctrl_gpio_request(gc->gpiodev->base + offset); } -@@ -1821,10 +2212,12 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); +@@ -1840,10 +2234,12 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); */ void gpiochip_generic_free(struct gpio_chip *gc, unsigned offset) { @@ -2728,7 +2708,7 @@ index 50abb1c20df0..cf7296db3190 100644 pinctrl_gpio_free(gc->gpiodev->base + offset); } -@@ -1839,11 +2232,13 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free); +@@ -1858,11 +2254,13 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free); int gpiochip_generic_config(struct gpio_chip *gc, unsigned offset, unsigned long config) { @@ -2743,7 +2723,7 @@ index 50abb1c20df0..cf7296db3190 100644 /** * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping -@@ -1865,6 +2260,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, +@@ -1884,6 +2282,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, struct gpio_device *gdev = gc->gpiodev; int ret; @@ -2752,7 +2732,7 @@ index 50abb1c20df0..cf7296db3190 100644 pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); if (!pin_range) { chip_err(gc, "failed to allocate pin ranges\n"); -@@ -1923,6 +2320,8 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, +@@ -1942,6 +2342,8 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, struct gpio_device *gdev = gc->gpiodev; int ret; @@ -2761,7 +2741,7 @@ index 50abb1c20df0..cf7296db3190 100644 pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); if (!pin_range) { chip_err(gc, "failed to allocate pin ranges\n"); -@@ -1964,6 +2363,8 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) +@@ -1983,6 +2385,8 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) struct gpio_pin_range *pin_range, *tmp; struct gpio_device *gdev = gc->gpiodev; @@ -2770,7 +2750,7 @@ index 50abb1c20df0..cf7296db3190 100644 list_for_each_entry_safe(pin_range, tmp, &gdev->pin_ranges, node) { list_del(&pin_range->node); pinctrl_remove_gpio_range(pin_range->pctldev, -@@ -1973,7 +2374,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) +@@ -1992,7 +2396,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *gc) } EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); @@ -2779,7 +2759,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* These "optional" allocation calls help prevent drivers from stomping * on each other, and help provide better diagnostics in debugfs. -@@ -1987,6 +2388,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) +@@ -2006,6 +2410,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) bool hogged = false; unsigned offset; @@ -2788,7 +2768,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (label) { /* Free desc->label if already allocated. */ if (desc->label) { -@@ -2092,6 +2495,8 @@ int gpiod_request(struct gpio_desc *desc, const char *label) +@@ -2111,6 +2517,8 @@ int gpiod_request(struct gpio_desc *desc, const char *label) int ret = -EPROBE_DEFER; struct gpio_device *gdev; @@ -2797,7 +2777,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); gdev = desc->gdev; -@@ -2108,6 +2513,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label) +@@ -2127,6 +2535,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label) return ret; } @@ -2805,7 +2785,7 @@ index 50abb1c20df0..cf7296db3190 100644 static bool gpiod_free_commit(struct gpio_desc *desc) { -@@ -2115,6 +2521,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2134,6 +2543,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) unsigned long flags; struct gpio_chip *gc; @@ -2814,7 +2794,7 @@ index 50abb1c20df0..cf7296db3190 100644 might_sleep(); gpiod_unexport(desc); -@@ -2141,12 +2549,12 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2160,12 +2571,12 @@ static bool gpiod_free_commit(struct gpio_desc *desc) clear_bit(FLAG_EDGE_RISING, &desc->flags); clear_bit(FLAG_EDGE_FALLING, &desc->flags); clear_bit(FLAG_IS_HOGGED, &desc->flags); @@ -2831,7 +2811,7 @@ index 50abb1c20df0..cf7296db3190 100644 ret = true; } -@@ -2159,6 +2567,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) +@@ -2178,6 +2589,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) void gpiod_free(struct gpio_desc *desc) { @@ -2840,7 +2820,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (desc && desc->gdev && gpiod_free_commit(desc)) { module_put(desc->gdev->owner); put_device(&desc->gdev->dev); -@@ -2166,6 +2576,7 @@ void gpiod_free(struct gpio_desc *desc) +@@ -2185,6 +2598,7 @@ void gpiod_free(struct gpio_desc *desc) WARN_ON(extra_checks); } } @@ -2848,7 +2828,7 @@ index 50abb1c20df0..cf7296db3190 100644 /** * gpiochip_is_requested - return string iff signal was requested -@@ -2184,6 +2595,8 @@ const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned offset) +@@ -2203,6 +2617,8 @@ const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned offset) { struct gpio_desc *desc; @@ -2857,7 +2837,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (offset >= gc->ngpio) return NULL; -@@ -2227,6 +2640,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, +@@ -2246,6 +2662,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, struct gpio_desc *desc = gpiochip_get_desc(gc, hwnum); int ret; @@ -2866,7 +2846,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (IS_ERR(desc)) { chip_err(gc, "failed to get GPIO descriptor\n"); return desc; -@@ -2274,6 +2689,8 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); +@@ -2293,6 +2711,8 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); static int gpio_do_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config) { @@ -2875,7 +2855,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!gc->set_config) return -ENOTSUPP; -@@ -2286,6 +2703,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) +@@ -2305,6 +2725,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) unsigned long config; unsigned arg; @@ -2884,7 +2864,7 @@ index 50abb1c20df0..cf7296db3190 100644 switch (mode) { case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_UP: -@@ -2305,6 +2724,8 @@ static int gpio_set_bias(struct gpio_desc *desc) +@@ -2324,6 +2746,8 @@ static int gpio_set_bias(struct gpio_desc *desc) int bias = 0; int ret = 0; @@ -2893,7 +2873,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) bias = PIN_CONFIG_BIAS_DISABLE; else if (test_bit(FLAG_PULL_UP, &desc->flags)) -@@ -2334,6 +2755,8 @@ int gpiod_direction_input(struct gpio_desc *desc) +@@ -2353,6 +2777,8 @@ int gpiod_direction_input(struct gpio_desc *desc) struct gpio_chip *gc; int ret = 0; @@ -2902,7 +2882,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); gc = desc->gdev->chip; -@@ -2381,6 +2804,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) +@@ -2400,6 +2826,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) int val = !!value; int ret = 0; @@ -2911,7 +2891,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* * It's OK not to specify .direction_output() if the gpiochip is * output-only, but if there is then not even a .set() operation it -@@ -2431,6 +2856,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) +@@ -2450,6 +2878,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) */ int gpiod_direction_output_raw(struct gpio_desc *desc, int value) { @@ -2920,7 +2900,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); return gpiod_direction_output_raw_commit(desc, value); } -@@ -2452,6 +2879,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) +@@ -2471,6 +2901,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) { int ret; @@ -2929,7 +2909,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; -@@ -2523,6 +2952,8 @@ int gpiod_timestamp_control(struct gpio_desc *desc, int enable) +@@ -2542,6 +2974,8 @@ int gpiod_timestamp_control(struct gpio_desc *desc, int enable) { struct gpio_chip *chip; @@ -2938,7 +2918,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); chip = desc->gdev->chip; if (!chip->timestamp_control) { -@@ -2550,6 +2981,8 @@ int gpiod_timestamp_read(struct gpio_desc *desc, u64 *ts) +@@ -2569,6 +3003,8 @@ int gpiod_timestamp_read(struct gpio_desc *desc, u64 *ts) u64 gpio_ts; int ret; @@ -2947,7 +2927,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); chip = desc->gdev->chip; if (!chip->timestamp_read) { -@@ -2578,6 +3011,8 @@ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) +@@ -2597,6 +3033,8 @@ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) { struct gpio_chip *gc; @@ -2956,7 +2936,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); gc = desc->gdev->chip; -@@ -2598,6 +3033,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) +@@ -2617,6 +3055,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) { unsigned long config; @@ -2965,7 +2945,7 @@ index 50abb1c20df0..cf7296db3190 100644 config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); return gpiod_set_config(desc, config); } -@@ -2618,6 +3055,8 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) +@@ -2637,6 +3077,8 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) int gpio; int rc; @@ -2974,7 +2954,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); /* * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for -@@ -2652,6 +3091,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_transitory); +@@ -2671,6 +3113,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_transitory); */ int gpiod_is_active_low(const struct gpio_desc *desc) { @@ -2983,7 +2963,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); return test_bit(FLAG_ACTIVE_LOW, &desc->flags); } -@@ -2663,6 +3104,8 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low); +@@ -2682,6 +3126,8 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low); */ void gpiod_toggle_active_low(struct gpio_desc *desc) { @@ -2992,7 +2972,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC_VOID(desc); change_bit(FLAG_ACTIVE_LOW, &desc->flags); } -@@ -2696,6 +3139,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) +@@ -2715,6 +3161,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) int offset; int value; @@ -3001,7 +2981,7 @@ index 50abb1c20df0..cf7296db3190 100644 gc = desc->gdev->chip; offset = gpio_chip_hwgpio(desc); value = gc->get ? gc->get(gc, offset) : -EIO; -@@ -2707,6 +3152,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) +@@ -2726,6 +3174,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) static int gpio_chip_get_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { @@ -3010,7 +2990,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (gc->get_multiple) { return gc->get_multiple(gc, mask, bits); } else if (gc->get) { -@@ -2731,6 +3178,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, +@@ -2750,6 +3200,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, { int ret, i = 0; @@ -3019,7 +2999,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* * Validate array_info against desc_array and its size. * It should immediately follow desc_array if both -@@ -2837,6 +3286,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, +@@ -2856,6 +3308,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, */ int gpiod_get_raw_value(const struct gpio_desc *desc) { @@ -3028,7 +3008,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); /* Should be using gpiod_get_raw_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -2858,6 +3309,8 @@ int gpiod_get_value(const struct gpio_desc *desc) +@@ -2877,6 +3331,8 @@ int gpiod_get_value(const struct gpio_desc *desc) { int value; @@ -3037,7 +3017,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); /* Should be using gpiod_get_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -2892,6 +3345,8 @@ int gpiod_get_raw_array_value(unsigned int array_size, +@@ -2911,6 +3367,8 @@ int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3046,7 +3026,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(true, false, array_size, -@@ -2918,6 +3373,8 @@ int gpiod_get_array_value(unsigned int array_size, +@@ -2937,6 +3395,8 @@ int gpiod_get_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3055,7 +3035,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(false, false, array_size, -@@ -2937,6 +3394,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) +@@ -2956,6 +3416,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) struct gpio_chip *gc = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); @@ -3064,7 +3044,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (value) { ret = gc->direction_input(gc, offset); } else { -@@ -2962,6 +3421,8 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value +@@ -2981,6 +3443,8 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value struct gpio_chip *gc = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); @@ -3073,7 +3053,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (value) { ret = gc->direction_output(gc, offset, 1); if (!ret) -@@ -2980,6 +3441,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) +@@ -2999,6 +3463,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) { struct gpio_chip *gc; @@ -3082,7 +3062,7 @@ index 50abb1c20df0..cf7296db3190 100644 gc = desc->gdev->chip; trace_gpio_value(desc_to_gpio(desc), 0, value); gc->set(gc, gpio_chip_hwgpio(desc), value); -@@ -2998,6 +3461,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) +@@ -3017,6 +3483,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) static void gpio_chip_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { @@ -3091,7 +3071,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (gc->set_multiple) { gc->set_multiple(gc, mask, bits); } else { -@@ -3017,6 +3482,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, +@@ -3036,6 +3504,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, { int i = 0; @@ -3100,7 +3080,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* * Validate array_info against desc_array and its size. * It should immediately follow desc_array if both -@@ -3122,6 +3589,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, +@@ -3141,6 +3611,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, */ void gpiod_set_raw_value(struct gpio_desc *desc, int value) { @@ -3109,7 +3089,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_raw_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -3140,6 +3609,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); +@@ -3159,6 +3631,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); */ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) { @@ -3118,7 +3098,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) -@@ -3163,6 +3634,8 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) +@@ -3182,6 +3656,8 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) */ void gpiod_set_value(struct gpio_desc *desc, int value) { @@ -3127,7 +3107,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_value_cansleep() */ WARN_ON(desc->gdev->chip->can_sleep); -@@ -3188,6 +3661,8 @@ int gpiod_set_raw_array_value(unsigned int array_size, +@@ -3207,6 +3683,8 @@ int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3136,7 +3116,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(true, false, array_size, -@@ -3213,6 +3688,8 @@ int gpiod_set_array_value(unsigned int array_size, +@@ -3232,6 +3710,8 @@ int gpiod_set_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3145,7 +3125,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(false, false, array_size, -@@ -3228,6 +3705,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); +@@ -3247,6 +3727,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); */ int gpiod_cansleep(const struct gpio_desc *desc) { @@ -3154,7 +3134,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); return desc->gdev->chip->can_sleep; } -@@ -3240,6 +3719,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); +@@ -3259,6 +3741,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); */ int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name) { @@ -3163,7 +3143,7 @@ index 50abb1c20df0..cf7296db3190 100644 VALIDATE_DESC(desc); if (name) { name = kstrdup_const(name, GFP_KERNEL); -@@ -3266,6 +3747,8 @@ int gpiod_to_irq(const struct gpio_desc *desc) +@@ -3285,6 +3769,8 @@ int gpiod_to_irq(const struct gpio_desc *desc) struct gpio_chip *gc; int offset; @@ -3172,7 +3152,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* * Cannot VALIDATE_DESC() here as gpiod_to_irq() consumer semantics * requires this function to not return zero on an invalid descriptor -@@ -3300,6 +3783,8 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq); +@@ -3329,6 +3815,8 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq); int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc; @@ -3181,7 +3161,7 @@ index 50abb1c20df0..cf7296db3190 100644 desc = gpiochip_get_desc(gc, offset); if (IS_ERR(desc)) -@@ -3355,6 +3840,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3384,6 +3872,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc; @@ -3190,7 +3170,7 @@ index 50abb1c20df0..cf7296db3190 100644 desc = gpiochip_get_desc(gc, offset); if (IS_ERR(desc)) return; -@@ -3372,6 +3859,8 @@ void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3401,6 +3891,8 @@ void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc = gpiochip_get_desc(gc, offset); @@ -3199,7 +3179,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!IS_ERR(desc) && !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); -@@ -3382,6 +3871,8 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset) +@@ -3411,6 +3903,8 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc = gpiochip_get_desc(gc, offset); @@ -3208,7 +3188,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!IS_ERR(desc) && !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) { /* -@@ -3397,6 +3888,8 @@ EXPORT_SYMBOL_GPL(gpiochip_enable_irq); +@@ -3426,6 +3920,8 @@ EXPORT_SYMBOL_GPL(gpiochip_enable_irq); bool gpiochip_line_is_irq(struct gpio_chip *gc, unsigned int offset) { @@ -3217,7 +3197,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (offset >= gc->ngpio) return false; -@@ -3430,6 +3923,8 @@ EXPORT_SYMBOL_GPL(gpiochip_relres_irq); +@@ -3459,6 +3955,8 @@ EXPORT_SYMBOL_GPL(gpiochip_relres_irq); bool gpiochip_line_is_open_drain(struct gpio_chip *gc, unsigned int offset) { @@ -3226,7 +3206,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (offset >= gc->ngpio) return false; -@@ -3439,6 +3934,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); +@@ -3468,6 +3966,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); bool gpiochip_line_is_open_source(struct gpio_chip *gc, unsigned int offset) { @@ -3235,7 +3215,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (offset >= gc->ngpio) return false; -@@ -3448,6 +3945,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); +@@ -3477,6 +3977,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); bool gpiochip_line_is_persistent(struct gpio_chip *gc, unsigned int offset) { @@ -3244,7 +3224,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (offset >= gc->ngpio) return false; -@@ -3466,6 +3965,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); +@@ -3495,6 +3997,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); */ int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) { @@ -3253,7 +3233,7 @@ index 50abb1c20df0..cf7296db3190 100644 might_sleep_if(extra_checks); VALIDATE_DESC(desc); return gpiod_get_raw_value_commit(desc); -@@ -3485,6 +3986,8 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) +@@ -3514,6 +4018,8 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) { int value; @@ -3262,7 +3242,7 @@ index 50abb1c20df0..cf7296db3190 100644 might_sleep_if(extra_checks); VALIDATE_DESC(desc); value = gpiod_get_raw_value_commit(desc); -@@ -3516,6 +4019,8 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, +@@ -3545,6 +4051,8 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3271,7 +3251,7 @@ index 50abb1c20df0..cf7296db3190 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3542,6 +4047,8 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, +@@ -3571,6 +4079,8 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3280,7 +3260,7 @@ index 50abb1c20df0..cf7296db3190 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3563,6 +4070,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); +@@ -3592,6 +4102,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); */ void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { @@ -3289,7 +3269,7 @@ index 50abb1c20df0..cf7296db3190 100644 might_sleep_if(extra_checks); VALIDATE_DESC_VOID(desc); gpiod_set_raw_value_commit(desc, value); -@@ -3581,6 +4090,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); +@@ -3610,6 +4122,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); */ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { @@ -3298,7 +3278,7 @@ index 50abb1c20df0..cf7296db3190 100644 might_sleep_if(extra_checks); VALIDATE_DESC_VOID(desc); gpiod_set_value_nocheck(desc, value); -@@ -3604,6 +4115,8 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, +@@ -3633,6 +4147,8 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3307,7 +3287,7 @@ index 50abb1c20df0..cf7296db3190 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3621,6 +4134,8 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) +@@ -3650,6 +4166,8 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) { unsigned int i; @@ -3316,7 +3296,7 @@ index 50abb1c20df0..cf7296db3190 100644 mutex_lock(&gpio_lookup_lock); for (i = 0; i < n; i++) -@@ -3646,6 +4161,8 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, +@@ -3675,6 +4193,8 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap) { @@ -3325,7 +3305,7 @@ index 50abb1c20df0..cf7296db3190 100644 might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; -@@ -3661,6 +4178,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); +@@ -3690,6 +4210,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); */ void gpiod_add_lookup_table(struct gpiod_lookup_table *table) { @@ -3334,7 +3314,7 @@ index 50abb1c20df0..cf7296db3190 100644 mutex_lock(&gpio_lookup_lock); list_add_tail(&table->list, &gpio_lookup_list); -@@ -3675,6 +4194,8 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); +@@ -3704,6 +4226,8 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); */ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) { @@ -3343,7 +3323,7 @@ index 50abb1c20df0..cf7296db3190 100644 mutex_lock(&gpio_lookup_lock); list_del(&table->list); -@@ -3692,6 +4213,8 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) +@@ -3721,6 +4245,8 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) struct gpio_chip *gc; struct gpiod_hog *hog; @@ -3352,7 +3332,7 @@ index 50abb1c20df0..cf7296db3190 100644 mutex_lock(&gpio_machine_hogs_mutex); for (hog = &hogs[0]; hog->chip_label; hog++) { -@@ -3715,6 +4238,8 @@ static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) +@@ -3744,6 +4270,8 @@ static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) const char *dev_id = dev ? dev_name(dev) : NULL; struct gpiod_lookup_table *table; @@ -3361,7 +3341,7 @@ index 50abb1c20df0..cf7296db3190 100644 mutex_lock(&gpio_lookup_lock); list_for_each_entry(table, &gpio_lookup_list, list) { -@@ -3748,6 +4273,8 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, +@@ -3777,6 +4305,8 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, struct gpiod_lookup_table *table; struct gpiod_lookup *p; @@ -3370,7 +3350,7 @@ index 50abb1c20df0..cf7296db3190 100644 table = gpiod_find_lookup_table(dev); if (!table) return desc; -@@ -3813,6 +4340,8 @@ static int platform_gpio_count(struct device *dev, const char *con_id) +@@ -3842,6 +4372,8 @@ static int platform_gpio_count(struct device *dev, const char *con_id) struct gpiod_lookup *p; unsigned int count = 0; @@ -3379,7 +3359,7 @@ index 50abb1c20df0..cf7296db3190 100644 table = gpiod_find_lookup_table(dev); if (!table) return -ENOENT; -@@ -3858,6 +4387,8 @@ struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, +@@ -3887,6 +4419,8 @@ struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, char prop_name[32]; /* 32 is max size of property name */ unsigned int i; @@ -3388,7 +3368,7 @@ index 50abb1c20df0..cf7296db3190 100644 for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (con_id) snprintf(prop_name, sizeof(prop_name), "%s-%s", -@@ -3886,6 +4417,8 @@ int gpiod_count(struct device *dev, const char *con_id) +@@ -3915,6 +4449,8 @@ int gpiod_count(struct device *dev, const char *con_id) { int count = -ENOENT; @@ -3397,7 +3377,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) count = of_gpio_get_count(dev, con_id); else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) -@@ -3911,6 +4444,8 @@ EXPORT_SYMBOL_GPL(gpiod_count); +@@ -3940,6 +4476,8 @@ EXPORT_SYMBOL_GPL(gpiod_count); struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags) { @@ -3406,7 +3386,7 @@ index 50abb1c20df0..cf7296db3190 100644 return gpiod_get_index(dev, con_id, 0, flags); } EXPORT_SYMBOL_GPL(gpiod_get); -@@ -3951,6 +4486,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, +@@ -3980,6 +4518,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, { int ret; @@ -3415,7 +3395,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (lflags & GPIO_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); -@@ -4121,6 +4658,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, +@@ -4150,6 +4690,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, struct gpio_desc *desc = ERR_PTR(-ENODEV); int ret; @@ -3424,7 +3404,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (!fwnode) return ERR_PTR(-EINVAL); -@@ -4178,6 +4717,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, +@@ -4207,6 +4749,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, { struct gpio_desc *desc; @@ -3433,7 +3413,7 @@ index 50abb1c20df0..cf7296db3190 100644 desc = gpiod_get_index(dev, con_id, index, flags); if (IS_ERR(desc)) { if (PTR_ERR(desc) == -ENOENT) -@@ -4204,6 +4745,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, +@@ -4233,6 +4777,8 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, int hwnum; int ret; @@ -3442,7 +3422,7 @@ index 50abb1c20df0..cf7296db3190 100644 gc = gpiod_to_chip(desc); hwnum = gpio_chip_hwgpio(desc); -@@ -4235,6 +4778,8 @@ static void gpiochip_free_hogs(struct gpio_chip *gc) +@@ -4264,6 +4810,8 @@ static void gpiochip_free_hogs(struct gpio_chip *gc) { int id; @@ -3451,7 +3431,7 @@ index 50abb1c20df0..cf7296db3190 100644 for (id = 0; id < gc->ngpio; id++) { if (test_bit(FLAG_IS_HOGGED, &gc->gpiodev->descs[id].flags)) gpiochip_free_own_desc(&gc->gpiodev->descs[id]); -@@ -4263,6 +4808,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, +@@ -4292,6 +4840,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, struct gpio_chip *gc; int count, bitmap_size; @@ -3460,7 +3440,7 @@ index 50abb1c20df0..cf7296db3190 100644 count = gpiod_count(dev, con_id); if (count < 0) return ERR_PTR(count); -@@ -4383,6 +4930,8 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, +@@ -4412,6 +4962,8 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, { struct gpio_descs *descs; @@ -3469,7 +3449,7 @@ index 50abb1c20df0..cf7296db3190 100644 descs = gpiod_get_array(dev, con_id, flags); if (PTR_ERR(descs) == -ENOENT) return NULL; -@@ -4399,6 +4948,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); +@@ -4428,6 +4980,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); */ void gpiod_put(struct gpio_desc *desc) { @@ -3478,7 +3458,7 @@ index 50abb1c20df0..cf7296db3190 100644 if (desc) gpiod_free(desc); } -@@ -4412,6 +4963,8 @@ void gpiod_put_array(struct gpio_descs *descs) +@@ -4441,6 +4995,8 @@ void gpiod_put_array(struct gpio_descs *descs) { unsigned int i; @@ -3487,7 +3467,7 @@ index 50abb1c20df0..cf7296db3190 100644 for (i = 0; i < descs->ndescs; i++) gpiod_put(descs->desc[i]); -@@ -4423,6 +4976,8 @@ static int __init gpiolib_dev_init(void) +@@ -4452,6 +5008,8 @@ static int __init gpiolib_dev_init(void) { int ret; @@ -3496,7 +3476,7 @@ index 50abb1c20df0..cf7296db3190 100644 /* Register GPIO sysfs bus */ ret = bus_register(&gpio_bus_type); if (ret < 0) { -@@ -4442,13 +4997,13 @@ static int __init gpiolib_dev_init(void) +@@ -4471,13 +5029,13 @@ static int __init gpiolib_dev_init(void) #if IS_ENABLED(CONFIG_OF_DYNAMIC) && IS_ENABLED(CONFIG_OF_GPIO) WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier)); @@ -3512,12 +3492,12 @@ index 50abb1c20df0..cf7296db3190 100644 static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) { -@@ -4575,4 +5130,4 @@ static int __init gpiolib_debugfs_init(void) +@@ -4604,4 +5162,4 @@ static int __init gpiolib_debugfs_init(void) } subsys_initcall(gpiolib_debugfs_init); -#endif /* DEBUG_FS */ -+ #endif /* FIXIT -- debug_FS */ ++ #endif /* DEBUG_FS */ diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index c73b34e03aae..4c46d7faac5e 100644 --- a/drivers/pinctrl/core.c diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch index edeb89aa8..4741d6c73 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0004-gpio-virt-drivers.patch @@ -15,13 +15,15 @@ index dcecc9f6e33f..ed7d58d68ba6 100644 endmenu diff --git a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile 2024-05-02 12:06:58.097355696 +0000 -+++ b/drivers/Makefile 2024-08-01 14:32:02.521803787 +0000 -@@ -193,0 +194,5 @@ ++++ b/drivers/Makefile 2024-09-03 09:17:29.592284124 +0000 +@@ -193,0 +194,7 @@ +# +# +# ++$(info Building GPIO proxy modules...) +obj-y += gpio-host-proxy/ +obj-y += gpio-guest-proxy/ ++$(info Finished building GPIO proxy modules...) diff --git a/drivers/gpio-guest-proxy/Kconfig b/drivers/gpio-guest-proxy/Kconfig new file mode 100644 index 0000000..e25a19a @@ -48,10 +50,10 @@ index 0000000..2580e02 +obj-$(CONFIG_TEGRA_GPIO_GUEST_PROXY) += gpio-guest-proxy.o diff --git a/drivers/gpio-guest-proxy/gpio-guest-proxy.c b/drivers/gpio-guest-proxy/gpio-guest-proxy.c new file mode 100644 -index 0000000..6fbc960 +index 0000000..e7215dd --- /dev/null +++ b/drivers/gpio-guest-proxy/gpio-guest-proxy.c -@@ -0,0 +1,909 @@ +@@ -0,0 +1,905 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO Guest Proxy Kernel Module @@ -408,7 +410,7 @@ index 0000000..6fbc960 + return ret; +} + -+// unpreserve_all_tegrachips also does unhooking ? ++// unpreserve_all_tegrachips also does unhooking +extern void unpreserve_all_tegrachips(void); +struct gpio_chip * find_chip_by_id(int id); + @@ -512,15 +514,13 @@ index 0000000..6fbc960 +/* + * Removes module, sends appropriate message to kernel + */ -+void tegra_gpio_guest_cleanup(void) ++int tegra_gpio_guest_cleanup(void) +{ + deb_info("removing module.\n"); + + // unmap iomem + iounmap((void __iomem*)gpio_vpa); + -+ // gpio_unhook is called by unpreserve_all_tegrachips() -+ // gpio_unhook() + // clean up shared memory with stock driver and unhook all functions + unpreserve_all_tegrachips(); + @@ -532,11 +532,9 @@ index 0000000..6fbc960 + unregister_chrdev(major_number, DEVICE_NAME); + + is_set_up = false; -+ return; ++ return 0; +} -+ -+ -+ ++EXPORT_SYMBOL_GPL(tegra_gpio_guest_cleanup); + +/* + * Opens device module, sends appropriate message to kernel @@ -986,10 +984,10 @@ index 0000000..c2e0184 +obj-$(CONFIG_TEGRA_GPIO_HOST_PROXY) += gpio-host-proxy.o diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.c b/drivers/gpio-host-proxy/gpio-host-proxy.c new file mode 100644 -index 0000000..399befa +index 0000000..e9d1e72 --- /dev/null +++ b/drivers/gpio-host-proxy/gpio-host-proxy.c -@@ -0,0 +1,805 @@ +@@ -0,0 +1,821 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * NVIDIA GPIO host Proxy Kernel Module @@ -1229,10 +1227,10 @@ index 0000000..399befa + major_number = register_chrdev(0, DEVICE_NAME, &fops); + if (major_number < 0) + { -+ deb_error("could not register number.\n"); ++ deb_error("chardev could not register number.\n"); + return major_number; + } -+ deb_debug("registered correctly with major number %d", major_number); ++ deb_debug("chardev registered correctly with major number %d", major_number); + + // Register the device class + gpio_host_proxy_class = class_create(THIS_MODULE, CLASS_NAME); @@ -1269,8 +1267,7 @@ index 0000000..399befa + class_unregister(gpio_host_proxy_class); // unregister the device class + class_destroy(gpio_host_proxy_class); // remove the device class + unregister_chrdev(major_number, DEVICE_NAME); // unregister the major number -+ deb_info("Goodbye from the GPIO passthrough\n"); -+ unregister_chrdev(major_number, DEVICE_NAME); ++ deb_info("Goodbye from GPIO passthrough\n"); + return 0; +} + @@ -1753,7 +1750,22 @@ index 0000000..399befa + return len; // return length of read data +} + -+/* module creation -- see also gpio_host_proxy_probe and gpio_host_proxy_remove */ ++int tegra_gpio_host_init(void) ++{ ++ struct platform_device *pdev = NULL; // not used -- param is for the kernel module version of code ++ return gpio_host_proxy_probe(pdev); ++} ++EXPORT_SYMBOL_GPL(tegra_gpio_host_init); ++ ++int tegra_gpio_host_cleanup(void) ++{ ++ struct platform_device *pdev = NULL; // not used -- param is for the kernel module version of code ++ return gpio_host_proxy_remove(pdev); ++} ++EXPORT_SYMBOL_GPL(tegra_gpio_host_cleanup); ++ ++/* proxy driver as module removed ++ * module creation -- see also gpio_host_proxy_probe and gpio_host_proxy_remove + +static const struct of_device_id gpio_host_proxy_ids[] = { + { .compatible = "nvidia,gpio-host-proxy" }, @@ -1795,6 +1807,8 @@ index 0000000..399befa + +module_init(gpio_host_proxy_init); +module_exit(gpio_host_proxy_exit); ++ ++*/ diff --git a/drivers/gpio-host-proxy/gpio-host-proxy.h b/drivers/gpio-host-proxy/gpio-host-proxy.h new file mode 100644 index 0000000..295c2b6 diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0006-defconfig-kernel.patch b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0006-defconfig-kernel.patch index cc906b8b0..e69de29bb 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0006-defconfig-kernel.patch +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/common/gpio-virt-common/patches/0006-defconfig-kernel.patch @@ -1,8481 +0,0 @@ -diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig -index bda2dff571c4..73a92ece4ca0 100644 ---- a/arch/arm64/configs/defconfig -+++ b/arch/arm64/configs/defconfig -@@ -1,201 +1,1283 @@ -+# -+# Automatically generated file; DO NOT EDIT. -+# Linux/arm64 5.10.104 Kernel Configuration -+# -+CONFIG_CC_VERSION_TEXT="gcc (GCC) 9.5.0" -+CONFIG_CC_IS_GCC=y -+CONFIG_GCC_VERSION=90500 -+CONFIG_LD_VERSION=240000000 -+CONFIG_CLANG_VERSION=0 -+CONFIG_LLD_VERSION=0 -+CONFIG_CC_CAN_LINK=y -+CONFIG_CC_HAS_ASM_GOTO=y -+CONFIG_CC_HAS_ASM_INLINE=y -+CONFIG_IRQ_WORK=y -+CONFIG_BUILDTIME_TABLE_SORT=y -+CONFIG_THREAD_INFO_IN_TASK=y -+ -+# -+# General setup -+# -+CONFIG_INIT_ENV_ARG_LIMIT=32 -+# CONFIG_COMPILE_TEST is not set -+CONFIG_LOCALVERSION="" - # CONFIG_LOCALVERSION_AUTO is not set -+CONFIG_BUILD_SALT="" -+CONFIG_DEFAULT_INIT="" -+CONFIG_DEFAULT_HOSTNAME="(none)" -+CONFIG_SWAP=y - CONFIG_SYSVIPC=y -+CONFIG_SYSVIPC_SYSCTL=y - CONFIG_POSIX_MQUEUE=y -+CONFIG_POSIX_MQUEUE_SYSCTL=y - CONFIG_WATCH_QUEUE=y -+CONFIG_CROSS_MEMORY_ATTACH=y -+# CONFIG_USELIB is not set - CONFIG_AUDIT=y -+CONFIG_HAVE_ARCH_AUDITSYSCALL=y -+CONFIG_AUDITSYSCALL=y -+ -+# -+# IRQ subsystem -+# -+CONFIG_GENERIC_IRQ_PROBE=y -+CONFIG_GENERIC_IRQ_SHOW=y -+CONFIG_GENERIC_IRQ_SHOW_LEVEL=y -+CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y -+CONFIG_GENERIC_IRQ_MIGRATION=y -+CONFIG_HARDIRQS_SW_RESEND=y -+CONFIG_IRQ_DOMAIN=y -+CONFIG_IRQ_SIM=y -+CONFIG_IRQ_DOMAIN_HIERARCHY=y -+CONFIG_GENERIC_IRQ_IPI=y -+CONFIG_GENERIC_MSI_IRQ=y -+CONFIG_GENERIC_MSI_IRQ_DOMAIN=y -+CONFIG_IRQ_MSI_IOMMU=y -+CONFIG_HANDLE_DOMAIN_IRQ=y -+CONFIG_IRQ_FORCED_THREADING=y -+CONFIG_SPARSE_IRQ=y -+# CONFIG_GENERIC_IRQ_DEBUGFS is not set -+# end of IRQ subsystem -+ -+CONFIG_GENERIC_IRQ_MULTI_HANDLER=y -+CONFIG_GENERIC_TIME_VSYSCALL=y -+CONFIG_GENERIC_CLOCKEVENTS=y -+CONFIG_ARCH_HAS_TICK_BROADCAST=y -+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y -+ -+# -+# Timers subsystem -+# -+CONFIG_TICK_ONESHOT=y -+CONFIG_NO_HZ_COMMON=y -+# CONFIG_HZ_PERIODIC is not set -+CONFIG_NO_HZ_IDLE=y -+# CONFIG_NO_HZ_FULL is not set - CONFIG_NO_HZ=y - CONFIG_HIGH_RES_TIMERS=y -+# end of Timers subsystem -+ -+# CONFIG_PREEMPT_NONE is not set -+# CONFIG_PREEMPT_VOLUNTARY is not set - CONFIG_PREEMPT=y -+CONFIG_PREEMPT_COUNT=y -+CONFIG_PREEMPTION=y -+ -+# -+# CPU/Task time and stats accounting -+# -+CONFIG_TICK_CPU_ACCOUNTING=y -+# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set - CONFIG_IRQ_TIME_ACCOUNTING=y -+CONFIG_HAVE_SCHED_AVG_IRQ=y -+CONFIG_SCHED_THERMAL_PRESSURE=y - CONFIG_BSD_PROCESS_ACCT=y - CONFIG_BSD_PROCESS_ACCT_V3=y -+CONFIG_TASKSTATS=y -+CONFIG_TASK_DELAY_ACCT=y - CONFIG_TASK_XACCT=y - CONFIG_TASK_IO_ACCOUNTING=y -+# CONFIG_PSI is not set -+# end of CPU/Task time and stats accounting -+ -+CONFIG_CPU_ISOLATION=y -+ -+# -+# RCU Subsystem -+# -+CONFIG_TREE_RCU=y -+CONFIG_PREEMPT_RCU=y -+# CONFIG_RCU_EXPERT is not set -+CONFIG_SRCU=y -+CONFIG_TREE_SRCU=y -+CONFIG_TASKS_RCU_GENERIC=y -+CONFIG_TASKS_RCU=y -+CONFIG_TASKS_RUDE_RCU=y -+CONFIG_TASKS_TRACE_RCU=y -+CONFIG_RCU_STALL_COMMON=y -+CONFIG_RCU_NEED_SEGCBLIST=y -+# end of RCU Subsystem -+ - CONFIG_IKCONFIG=y - CONFIG_IKCONFIG_PROC=y -+# CONFIG_IKHEADERS is not set -+CONFIG_LOG_BUF_SHIFT=20 -+CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 -+CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=14 -+CONFIG_GENERIC_SCHED_CLOCK=y -+ -+# -+# Scheduler features -+# -+# CONFIG_UCLAMP_TASK is not set -+# end of Scheduler features -+ -+CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y -+CONFIG_CC_HAS_INT128=y -+CONFIG_ARCH_SUPPORTS_INT128=y - CONFIG_CGROUPS=y -+CONFIG_PAGE_COUNTER=y - CONFIG_MEMCG=y -+CONFIG_MEMCG_SWAP=y -+CONFIG_MEMCG_KMEM=y - CONFIG_BLK_CGROUP=y -+CONFIG_CGROUP_WRITEBACK=y - CONFIG_CGROUP_SCHED=y -+CONFIG_FAIR_GROUP_SCHED=y - CONFIG_CFS_BANDWIDTH=y - CONFIG_RT_GROUP_SCHED=y - CONFIG_CGROUP_PIDS=y -+# CONFIG_CGROUP_RDMA is not set - CONFIG_CGROUP_FREEZER=y - CONFIG_CGROUP_HUGETLB=y - CONFIG_CPUSETS=y -+CONFIG_PROC_PID_CPUSET=y - CONFIG_CGROUP_DEVICE=y - CONFIG_CGROUP_CPUACCT=y - CONFIG_CGROUP_PERF=y -+# CONFIG_CGROUP_BPF is not set -+# CONFIG_CGROUP_DEBUG is not set -+CONFIG_SOCK_CGROUP_DATA=y - CONFIG_NAMESPACES=y -+CONFIG_UTS_NS=y -+CONFIG_TIME_NS=y -+CONFIG_IPC_NS=y - CONFIG_USER_NS=y -+CONFIG_PID_NS=y -+CONFIG_NET_NS=y -+# CONFIG_CHECKPOINT_RESTORE is not set -+# CONFIG_SCHED_AUTOGROUP is not set -+# CONFIG_SYSFS_DEPRECATED is not set - CONFIG_RELAY=y - CONFIG_BLK_DEV_INITRD=y -+CONFIG_INITRAMFS_SOURCE="" -+CONFIG_RD_GZIP=y -+CONFIG_RD_BZIP2=y -+CONFIG_RD_LZMA=y -+CONFIG_RD_XZ=y -+CONFIG_RD_LZO=y -+CONFIG_RD_LZ4=y -+CONFIG_RD_ZSTD=y -+# CONFIG_BOOT_CONFIG is not set -+CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y -+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -+CONFIG_LD_ORPHAN_WARN=y -+CONFIG_SYSCTL=y -+CONFIG_HAVE_UID16=y -+CONFIG_SYSCTL_EXCEPTION_TRACE=y -+CONFIG_BPF=y -+CONFIG_EXPERT=y -+CONFIG_UID16=y -+CONFIG_MULTIUSER=y -+# CONFIG_SGETMASK_SYSCALL is not set -+CONFIG_SYSFS_SYSCALL=y -+CONFIG_FHANDLE=y -+CONFIG_POSIX_TIMERS=y -+CONFIG_PRINTK=y -+CONFIG_PRINTK_NMI=y -+CONFIG_BUG=y -+CONFIG_ELF_CORE=y -+CONFIG_BASE_FULL=y -+CONFIG_FUTEX=y -+CONFIG_FUTEX_PI=y -+CONFIG_HAVE_FUTEX_CMPXCHG=y -+CONFIG_EPOLL=y -+CONFIG_SIGNALFD=y -+CONFIG_TIMERFD=y -+CONFIG_EVENTFD=y -+CONFIG_SHMEM=y -+CONFIG_AIO=y -+CONFIG_IO_URING=y -+CONFIG_ADVISE_SYSCALLS=y -+CONFIG_MEMBARRIER=y -+CONFIG_KALLSYMS=y - CONFIG_KALLSYMS_ALL=y -+CONFIG_KALLSYMS_BASE_RELATIVE=y -+CONFIG_BPF_SYSCALL=y -+CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y -+# CONFIG_BPF_JIT_ALWAYS_ON is not set -+CONFIG_BPF_JIT_DEFAULT_ON=y -+# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set -+# CONFIG_BPF_PRELOAD is not set -+# CONFIG_USERFAULTFD is not set -+CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y -+CONFIG_KCMP=y -+CONFIG_RSEQ=y -+# CONFIG_DEBUG_RSEQ is not set - CONFIG_EMBEDDED=y -+CONFIG_HAVE_PERF_EVENTS=y -+# CONFIG_PC104 is not set -+ -+# -+# Kernel Performance Events And Counters -+# -+CONFIG_PERF_EVENTS=y -+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set -+# end of Kernel Performance Events And Counters -+ -+CONFIG_VM_EVENT_COUNTERS=y -+CONFIG_SLUB_DEBUG=y -+# CONFIG_SLUB_MEMCG_SYSFS_ON is not set - # CONFIG_COMPAT_BRK is not set -+# CONFIG_SLAB is not set -+CONFIG_SLUB=y -+# CONFIG_SLOB is not set -+CONFIG_SLAB_MERGE_DEFAULT=y - CONFIG_SLAB_FREELIST_RANDOM=y - CONFIG_SLAB_FREELIST_HARDENED=y - CONFIG_SHUFFLE_PAGE_ALLOCATOR=y -+CONFIG_SLUB_CPU_PARTIAL=y -+CONFIG_SYSTEM_DATA_VERIFICATION=y - CONFIG_PROFILING=y -+CONFIG_TRACEPOINTS=y -+# end of General setup -+ -+CONFIG_ARM64=y -+CONFIG_64BIT=y -+CONFIG_MMU=y -+CONFIG_ARM64_PAGE_SHIFT=12 -+CONFIG_ARM64_CONT_PTE_SHIFT=4 -+CONFIG_ARM64_CONT_PMD_SHIFT=4 -+CONFIG_ARCH_MMAP_RND_BITS_MIN=18 -+CONFIG_ARCH_MMAP_RND_BITS_MAX=33 -+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 -+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16 -+CONFIG_STACKTRACE_SUPPORT=y -+CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 -+CONFIG_LOCKDEP_SUPPORT=y -+CONFIG_TRACE_IRQFLAGS_SUPPORT=y -+CONFIG_GENERIC_BUG=y -+CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y -+CONFIG_GENERIC_HWEIGHT=y -+CONFIG_GENERIC_CSUM=y -+CONFIG_GENERIC_CALIBRATE_DELAY=y -+CONFIG_ZONE_DMA=y -+CONFIG_ZONE_DMA32=y -+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y -+CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y -+CONFIG_SMP=y -+CONFIG_KERNEL_MODE_NEON=y -+CONFIG_FIX_EARLYCON_MEM=y -+CONFIG_PGTABLE_LEVELS=4 -+CONFIG_ARCH_SUPPORTS_UPROBES=y -+CONFIG_ARCH_PROC_KCORE_TEXT=y -+ -+# -+# Platform selection -+# -+# CONFIG_ARCH_ACTIONS is not set -+# CONFIG_ARCH_AGILEX is not set -+# CONFIG_ARCH_SUNXI is not set -+# CONFIG_ARCH_ALPINE is not set -+# CONFIG_ARCH_BCM2835 is not set -+# CONFIG_ARCH_BCM_IPROC is not set -+# CONFIG_ARCH_BERLIN is not set -+# CONFIG_ARCH_BITMAIN is not set -+# CONFIG_ARCH_BRCMSTB is not set -+# CONFIG_ARCH_EXYNOS is not set -+# CONFIG_ARCH_SPARX5 is not set -+# CONFIG_ARCH_K3 is not set -+# CONFIG_ARCH_LAYERSCAPE is not set -+# CONFIG_ARCH_LG1K is not set -+# CONFIG_ARCH_HISI is not set -+# CONFIG_ARCH_KEEMBAY is not set -+# CONFIG_ARCH_MEDIATEK is not set -+# CONFIG_ARCH_MESON is not set -+# CONFIG_ARCH_MVEBU is not set -+# CONFIG_ARCH_MXC is not set -+# CONFIG_ARCH_QCOM is not set -+# CONFIG_ARCH_REALTEK is not set -+# CONFIG_ARCH_RENESAS is not set -+# CONFIG_ARCH_ROCKCHIP is not set -+# CONFIG_ARCH_S32 is not set -+# CONFIG_ARCH_SEATTLE is not set -+# CONFIG_ARCH_STRATIX10 is not set -+# CONFIG_ARCH_SYNQUACER is not set - CONFIG_ARCH_TEGRA=y -+# CONFIG_ARCH_SPRD is not set -+# CONFIG_ARCH_THUNDER is not set -+# CONFIG_ARCH_THUNDER2 is not set -+# CONFIG_ARCH_UNIPHIER is not set -+# CONFIG_ARCH_VEXPRESS is not set -+# CONFIG_ARCH_VISCONTI is not set -+# CONFIG_ARCH_XGENE is not set -+# CONFIG_ARCH_ZX is not set -+# CONFIG_ARCH_ZYNQMP is not set -+# end of Platform selection -+ -+# -+# Kernel Features -+# -+ -+# -+# ARM errata workarounds via the alternatives framework -+# -+CONFIG_ARM64_WORKAROUND_CLEAN_CACHE=y -+CONFIG_ARM64_ERRATUM_826319=y -+CONFIG_ARM64_ERRATUM_827319=y -+CONFIG_ARM64_ERRATUM_824069=y -+CONFIG_ARM64_ERRATUM_819472=y -+CONFIG_ARM64_ERRATUM_832075=y -+CONFIG_ARM64_ERRATUM_834220=y -+CONFIG_ARM64_ERRATUM_845719=y -+CONFIG_ARM64_ERRATUM_843419=y -+CONFIG_ARM64_ERRATUM_1024718=y -+CONFIG_ARM64_ERRATUM_1418040=y -+CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT=y -+CONFIG_ARM64_ERRATUM_1165522=y -+CONFIG_ARM64_ERRATUM_1319367=y -+CONFIG_ARM64_ERRATUM_1530923=y -+CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y -+CONFIG_ARM64_ERRATUM_1286807=y -+CONFIG_ARM64_ERRATUM_1463225=y -+CONFIG_ARM64_ERRATUM_1542419=y -+CONFIG_ARM64_ERRATUM_1508412=y -+CONFIG_CAVIUM_ERRATUM_22375=y -+CONFIG_CAVIUM_ERRATUM_23154=y -+CONFIG_CAVIUM_ERRATUM_27456=y -+CONFIG_CAVIUM_ERRATUM_30115=y -+CONFIG_CAVIUM_TX2_ERRATUM_219=y -+CONFIG_FUJITSU_ERRATUM_010001=y -+CONFIG_HISILICON_ERRATUM_161600802=y -+CONFIG_QCOM_FALKOR_ERRATUM_1003=y -+CONFIG_QCOM_FALKOR_ERRATUM_1009=y -+CONFIG_QCOM_QDF2400_ERRATUM_0065=y -+CONFIG_QCOM_FALKOR_ERRATUM_E1041=y -+CONFIG_NVIDIA_CARMEL_CNP_ERRATUM=y -+CONFIG_SOCIONEXT_SYNQUACER_PREITS=y -+# end of ARM errata workarounds via the alternatives framework -+ -+CONFIG_ARM64_4K_PAGES=y -+# CONFIG_ARM64_16K_PAGES is not set -+# CONFIG_ARM64_64K_PAGES is not set -+# CONFIG_ARM64_VA_BITS_39 is not set - CONFIG_ARM64_VA_BITS_48=y -+CONFIG_ARM64_VA_BITS=48 -+CONFIG_ARM64_PA_BITS_48=y -+CONFIG_ARM64_PA_BITS=48 -+# CONFIG_CPU_BIG_ENDIAN is not set -+CONFIG_CPU_LITTLE_ENDIAN=y - CONFIG_SCHED_MC=y - CONFIG_SCHED_SMT=y -+CONFIG_NR_CPUS=256 -+CONFIG_HOTPLUG_CPU=y -+# CONFIG_NUMA is not set -+CONFIG_HOLES_IN_ZONE=y -+# CONFIG_HZ_100 is not set -+CONFIG_HZ_250=y -+# CONFIG_HZ_300 is not set -+# CONFIG_HZ_1000 is not set -+CONFIG_HZ=250 -+CONFIG_SCHED_HRTICK=y -+CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y -+CONFIG_ARCH_SPARSEMEM_ENABLE=y -+CONFIG_ARCH_SPARSEMEM_DEFAULT=y -+CONFIG_ARCH_SELECT_MEMORY_MODEL=y -+CONFIG_ARCH_FLATMEM_ENABLE=y -+CONFIG_HAVE_ARCH_PFN_VALID=y -+CONFIG_HW_PERF_EVENTS=y -+CONFIG_SYS_SUPPORTS_HUGETLBFS=y -+CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y -+CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y -+CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y - CONFIG_PARAVIRT=y -+# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set - CONFIG_KEXEC=y - CONFIG_KEXEC_FILE=y -+# CONFIG_KEXEC_SIG is not set - CONFIG_CRASH_DUMP=y -+# CONFIG_XEN is not set -+CONFIG_FORCE_MAX_ZONEORDER=11 -+CONFIG_UNMAP_KERNEL_AT_EL0=y - # CONFIG_RODATA_FULL_DEFAULT_ENABLED is not set - CONFIG_ARM64_SW_TTBR0_PAN=y -+CONFIG_ARM64_TAGGED_ADDR_ABI=y - CONFIG_COMPAT=y -+CONFIG_KUSER_HELPERS=y - CONFIG_ARMV8_DEPRECATED=y - CONFIG_SWP_EMULATION=y - CONFIG_CP15_BARRIER_EMULATION=y - CONFIG_SETEND_EMULATION=y -+ -+# -+# ARMv8.1 architectural features -+# - # CONFIG_ARM64_HW_AFDBM is not set -+CONFIG_ARM64_PAN=y -+CONFIG_AS_HAS_LSE_ATOMICS=y -+CONFIG_ARM64_LSE_ATOMICS=y -+CONFIG_ARM64_USE_LSE_ATOMICS=y -+CONFIG_ARM64_VHE=y -+# end of ARMv8.1 architectural features -+ -+# -+# ARMv8.2 architectural features -+# -+CONFIG_ARM64_UAO=y -+# CONFIG_ARM64_PMEM is not set -+CONFIG_ARM64_RAS_EXTN=y -+CONFIG_ARM64_CNP=y -+# end of ARMv8.2 architectural features -+ -+# -+# ARMv8.3 architectural features -+# -+CONFIG_CC_HAS_BRANCH_PROT_PAC_RET=y -+CONFIG_CC_HAS_SIGN_RETURN_ADDRESS=y -+CONFIG_AS_HAS_PAC=y -+CONFIG_AS_HAS_CFI_NEGATE_RA_STATE=y -+# end of ARMv8.3 architectural features -+ -+# -+# ARMv8.4 architectural features -+# -+CONFIG_ARM64_AMU_EXTN=y -+# end of ARMv8.4 architectural features -+ -+# -+# ARMv8.5 architectural features -+# -+CONFIG_ARM64_BTI=y -+CONFIG_CC_HAS_BRANCH_PROT_PAC_RET_BTI=y -+CONFIG_ARM64_E0PD=y -+CONFIG_ARCH_RANDOM=y -+CONFIG_ARM64_AS_HAS_MTE=y -+CONFIG_ARM64_MTE=y -+# end of ARMv8.5 architectural features -+ -+CONFIG_ARM64_SVE=y -+CONFIG_ARM64_MODULE_PLTS=y -+# CONFIG_ARM64_PSEUDO_NMI is not set -+CONFIG_RELOCATABLE=y - CONFIG_RANDOMIZE_BASE=y -+CONFIG_RANDOMIZE_MODULE_REGION_FULL=y -+CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y -+CONFIG_STACKPROTECTOR_PER_TASK=y -+# CONFIG_TEGRA_EBP is not set -+CONFIG_TEGRA_PSC=y -+# end of Kernel Features -+ -+# -+# Boot options -+# -+# CONFIG_ARM64_ACPI_PARKING_PROTOCOL is not set -+CONFIG_CMDLINE="" -+CONFIG_EFI_STUB=y -+CONFIG_EFI=y -+CONFIG_DMI=y -+# end of Boot options -+ -+CONFIG_SYSVIPC_COMPAT=y -+CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y -+CONFIG_ARCH_ENABLE_THP_MIGRATION=y -+ -+# -+# Power management options -+# -+CONFIG_SUSPEND=y -+CONFIG_SUSPEND_FREEZER=y -+# CONFIG_SUSPEND_SKIP_SYNC is not set -+# CONFIG_HIBERNATION is not set -+CONFIG_PM_SLEEP=y -+CONFIG_PM_SLEEP_SMP=y - CONFIG_PM_AUTOSLEEP=y - CONFIG_PM_WAKELOCKS=y -+CONFIG_PM_WAKELOCKS_LIMIT=100 -+CONFIG_PM_WAKELOCKS_GC=y -+CONFIG_PM=y -+# CONFIG_PM_DEBUG is not set -+CONFIG_PM_CLK=y -+CONFIG_PM_GENERIC_DOMAINS=y - CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y -+CONFIG_PM_GENERIC_DOMAINS_SLEEP=y -+CONFIG_PM_GENERIC_DOMAINS_OF=y -+CONFIG_CPU_PM=y - CONFIG_ENERGY_MODEL=y -+CONFIG_ARCH_HIBERNATION_POSSIBLE=y -+CONFIG_ARCH_SUSPEND_POSSIBLE=y -+# end of Power management options -+ -+# -+# CPU Power Management -+# -+ -+# -+# CPU Idle -+# -+CONFIG_CPU_IDLE=y -+CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y -+# CONFIG_CPU_IDLE_GOV_LADDER is not set -+CONFIG_CPU_IDLE_GOV_MENU=y -+# CONFIG_CPU_IDLE_GOV_TEO is not set -+CONFIG_DT_IDLE_STATES=y -+ -+# -+# ARM CPU Idle Drivers -+# - CONFIG_ARM_CPUIDLE=y - CONFIG_ARM_PSCI_CPUIDLE=y -+CONFIG_ARM_PSCI_CPUIDLE_DOMAIN=y -+CONFIG_CPU_IDLE_TEGRA19X=y - CONFIG_CPU_IDLE_TEGRA_AUTO=y -+# end of ARM CPU Idle Drivers -+# end of CPU Idle -+ -+# -+# CPU Frequency scaling -+# - CONFIG_CPU_FREQ=y -+CONFIG_CPU_FREQ_GOV_ATTR_SET=y -+CONFIG_CPU_FREQ_GOV_COMMON=y - CONFIG_CPU_FREQ_STAT=y - CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y -+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set -+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set -+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set -+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set -+# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y - CONFIG_CPU_FREQ_GOV_POWERSAVE=y - CONFIG_CPU_FREQ_GOV_USERSPACE=y - CONFIG_CPU_FREQ_GOV_ONDEMAND=y - CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y - CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y -+ -+# -+# CPU frequency scaling drivers -+# - CONFIG_CPUFREQ_DT=y -+CONFIG_CPUFREQ_DT_PLATDEV=y - CONFIG_ACPI_CPPC_CPUFREQ=m -+CONFIG_ARM_TEGRA20_CPUFREQ=y -+CONFIG_ARM_TEGRA124_CPUFREQ=y - CONFIG_ARM_TEGRA186_CPUFREQ=y -+CONFIG_ARM_TEGRA194_CPUFREQ=y -+# end of CPU Frequency scaling -+# end of CPU Power Management -+ -+# -+# Firmware Drivers -+# -+# CONFIG_ARM_SCMI_PROTOCOL is not set - CONFIG_ARM_SCPI_PROTOCOL=m -+CONFIG_ARM_SCPI_POWER_DOMAIN=m -+# CONFIG_ARM_SDE_INTERFACE is not set -+# CONFIG_FIRMWARE_MEMMAP is not set -+CONFIG_DMIID=y - CONFIG_DMI_SYSFS=y -+# CONFIG_ISCSI_IBFT is not set -+# CONFIG_FW_CFG_SYSFS is not set -+# CONFIG_GOOGLE_FIRMWARE is not set -+ -+# -+# EFI (Extensible Firmware Interface) Support -+# -+CONFIG_EFI_ESRT=y - # CONFIG_EFI_VARS_PSTORE is not set -+CONFIG_EFI_PARAMS_FROM_FDT=y -+CONFIG_EFI_RUNTIME_WRAPPERS=y -+CONFIG_EFI_GENERIC_STUB=y -+CONFIG_EFI_ARMSTUB_DTB_LOADER=y -+CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y -+# CONFIG_EFI_BOOTLOADER_CONTROL is not set - CONFIG_EFI_CAPSULE_LOADER=y - CONFIG_EFI_TEST=m --CONFIG_FB_EFI=y -+# CONFIG_RESET_ATTACK_MITIGATION is not set -+# CONFIG_EFI_DISABLE_PCI_DMA is not set -+# end of EFI (Extensible Firmware Interface) Support -+ -+CONFIG_UEFI_CPER=y -+CONFIG_UEFI_CPER_ARM=y -+CONFIG_EFI_EARLYCON=y -+CONFIG_EFI_CUSTOM_SSDT_OVERLAYS=y -+CONFIG_ARM_PSCI_FW=y -+# CONFIG_ARM_PSCI_CHECKER is not set -+CONFIG_HAVE_ARM_SMCCC=y -+CONFIG_HAVE_ARM_SMCCC_DISCOVERY=y -+CONFIG_ARM_SMCCC_SOC_ID=y -+ -+# -+# Tegra firmware driver -+# -+CONFIG_TEGRA_IVC=y -+CONFIG_TEGRA_BPMP=y -+# end of Tegra firmware driver -+ -+# -+# Tegra BPMP Driver -+# -+# end of Tegra BPMP Driver -+ -+# -+# Tegra firmware driver -+# -+# end of Tegra firmware driver -+ -+# -+# Tegra BPMP Driver -+# -+# end of Tegra BPMP Driver -+# end of Firmware Drivers -+ -+CONFIG_ARCH_SUPPORTS_ACPI=y - CONFIG_ACPI=y -+CONFIG_ACPI_GENERIC_GSI=y -+CONFIG_ACPI_CCA_REQUIRED=y -+# CONFIG_ACPI_DEBUGGER is not set -+CONFIG_ACPI_SPCR_TABLE=y -+# CONFIG_ACPI_EC_DEBUGFS is not set -+CONFIG_ACPI_AC=y -+CONFIG_ACPI_BATTERY=y -+CONFIG_ACPI_BUTTON=y -+CONFIG_ACPI_FAN=y -+# CONFIG_ACPI_TAD is not set -+# CONFIG_ACPI_DOCK is not set -+CONFIG_ACPI_PROCESSOR_IDLE=y -+CONFIG_ACPI_MCFG=y -+CONFIG_ACPI_CPPC_LIB=y -+CONFIG_ACPI_PROCESSOR=y -+CONFIG_ACPI_HOTPLUG_CPU=y -+CONFIG_ACPI_THERMAL=y -+CONFIG_ARCH_HAS_ACPI_TABLE_UPGRADE=y -+CONFIG_ACPI_TABLE_UPGRADE=y -+# CONFIG_ACPI_DEBUG is not set -+# CONFIG_ACPI_PCI_SLOT is not set -+CONFIG_ACPI_CONTAINER=y -+CONFIG_ACPI_HED=y -+# CONFIG_ACPI_CUSTOM_METHOD is not set -+# CONFIG_ACPI_BGRT is not set -+CONFIG_ACPI_REDUCED_HARDWARE_ONLY=y -+CONFIG_HAVE_ACPI_APEI=y - CONFIG_ACPI_APEI=y - CONFIG_ACPI_APEI_GHES=y -+# CONFIG_ACPI_APEI_PCIEAER is not set -+CONFIG_ACPI_APEI_SEA=y - CONFIG_ACPI_APEI_MEMORY_FAILURE=y - CONFIG_ACPI_APEI_EINJ=y -+# CONFIG_ACPI_APEI_ERST_DEBUG is not set -+# CONFIG_ACPI_CONFIGFS is not set -+CONFIG_ACPI_IORT=y -+CONFIG_ACPI_GTDT=y -+CONFIG_ACPI_PPTT=y -+# CONFIG_PMIC_OPREGION is not set -+CONFIG_IRQ_BYPASS_MANAGER=y -+CONFIG_VIRTUALIZATION=y - CONFIG_KVM=y -+CONFIG_HAVE_KVM_IRQCHIP=y -+CONFIG_HAVE_KVM_IRQFD=y -+CONFIG_HAVE_KVM_IRQ_ROUTING=y -+CONFIG_HAVE_KVM_EVENTFD=y -+CONFIG_KVM_MMIO=y -+CONFIG_HAVE_KVM_MSI=y -+CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT=y -+CONFIG_KVM_VFIO=y -+CONFIG_HAVE_KVM_ARCH_TLB_FLUSH_ALL=y -+CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT=y -+CONFIG_HAVE_KVM_IRQ_BYPASS=y -+CONFIG_HAVE_KVM_VCPU_RUN_PID_CHANGE=y -+CONFIG_KVM_ARM_PMU=y - CONFIG_TEGRA_DTC_SUPPRESS_WARNINGS=y - CONFIG_ARM64_CRYPTO=y -+CONFIG_CRYPTO_SHA256_ARM64=m -+CONFIG_CRYPTO_SHA512_ARM64=m - CONFIG_CRYPTO_SHA1_ARM64_CE=m - CONFIG_CRYPTO_SHA2_ARM64_CE=m - CONFIG_CRYPTO_SHA512_ARM64_CE=m - CONFIG_CRYPTO_SHA3_ARM64=m - CONFIG_CRYPTO_SM3_ARM64_CE=m -+# CONFIG_CRYPTO_SM4_ARM64_CE is not set - CONFIG_CRYPTO_GHASH_ARM64_CE=m -+# CONFIG_CRYPTO_CRCT10DIF_ARM64_CE is not set -+CONFIG_CRYPTO_AES_ARM64=m -+CONFIG_CRYPTO_AES_ARM64_CE=m - CONFIG_CRYPTO_AES_ARM64_CE_CCM=m - CONFIG_CRYPTO_AES_ARM64_CE_BLK=m - CONFIG_CRYPTO_AES_ARM64_NEON_BLK=m -+# CONFIG_CRYPTO_CHACHA20_NEON is not set -+# CONFIG_CRYPTO_POLY1305_NEON is not set -+# CONFIG_CRYPTO_NHPOLY1305_NEON is not set -+# CONFIG_CRYPTO_AES_ARM64_BS is not set -+# CONFIG_ARCH_TEGRA_18x_SOC is not set -+# CONFIG_ARCH_TEGRA_19x_SOC is not set -+# CONFIG_ARCH_TEGRA_21x_SOC is not set - CONFIG_ARCH_TEGRA_23x_SOC=y --CONFIG_ARCH_TEGRA_239_SOC=y -+ -+# -+# General architecture-dependent options -+# -+CONFIG_CRASH_CORE=y -+CONFIG_KEXEC_CORE=y -+CONFIG_SET_FS=y -+# CONFIG_KPROBES is not set - CONFIG_JUMP_LABEL=y -+# CONFIG_STATIC_KEYS_SELFTEST is not set -+CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y -+CONFIG_HAVE_KPROBES=y -+CONFIG_HAVE_KRETPROBES=y -+CONFIG_HAVE_FUNCTION_ERROR_INJECTION=y -+CONFIG_HAVE_NMI=y -+CONFIG_HAVE_ARCH_TRACEHOOK=y -+CONFIG_HAVE_DMA_CONTIGUOUS=y -+CONFIG_GENERIC_SMP_IDLE_THREAD=y -+CONFIG_GENERIC_IDLE_POLL_SETUP=y -+CONFIG_ARCH_HAS_FORTIFY_SOURCE=y -+CONFIG_ARCH_HAS_KEEPINITRD=y -+CONFIG_ARCH_HAS_SET_MEMORY=y -+CONFIG_ARCH_HAS_SET_DIRECT_MAP=y -+CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y -+CONFIG_HAVE_ASM_MODVERSIONS=y -+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y -+CONFIG_HAVE_RSEQ=y -+CONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y -+CONFIG_HAVE_HW_BREAKPOINT=y -+CONFIG_HAVE_PERF_REGS=y -+CONFIG_HAVE_PERF_USER_STACK_DUMP=y -+CONFIG_HAVE_ARCH_JUMP_LABEL=y -+CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y -+CONFIG_MMU_GATHER_TABLE_FREE=y -+CONFIG_MMU_GATHER_RCU_TABLE_FREE=y -+CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y -+CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y -+CONFIG_HAVE_CMPXCHG_LOCAL=y -+CONFIG_HAVE_CMPXCHG_DOUBLE=y -+CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION=y -+CONFIG_HAVE_ARCH_SECCOMP=y -+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y -+CONFIG_SECCOMP=y -+CONFIG_SECCOMP_FILTER=y -+CONFIG_HAVE_ARCH_STACKLEAK=y -+CONFIG_HAVE_STACKPROTECTOR=y -+CONFIG_STACKPROTECTOR=y -+CONFIG_STACKPROTECTOR_STRONG=y -+CONFIG_HAVE_CONTEXT_TRACKING=y -+CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y -+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y -+CONFIG_HAVE_MOVE_PMD=y -+CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y -+CONFIG_HAVE_ARCH_HUGE_VMAP=y -+CONFIG_HAVE_MOD_ARCH_SPECIFIC=y -+CONFIG_MODULES_USE_ELF_RELA=y -+CONFIG_ARCH_HAS_ELF_RANDOMIZE=y -+CONFIG_HAVE_ARCH_MMAP_RND_BITS=y -+CONFIG_ARCH_MMAP_RND_BITS=18 -+CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS=y -+CONFIG_ARCH_MMAP_RND_COMPAT_BITS=11 -+CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y -+CONFIG_CLONE_BACKWARDS=y -+CONFIG_OLD_SIGSUSPEND3=y -+CONFIG_COMPAT_OLD_SIGACTION=y -+CONFIG_COMPAT_32BIT_TIME=y -+CONFIG_HAVE_ARCH_VMAP_STACK=y -+CONFIG_VMAP_STACK=y -+CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y -+CONFIG_STRICT_KERNEL_RWX=y -+CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y -+CONFIG_STRICT_MODULE_RWX=y -+CONFIG_HAVE_ARCH_COMPILER_H=y -+CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y -+CONFIG_ARCH_USE_MEMREMAP_PROT=y -+# CONFIG_LOCK_EVENT_COUNTS is not set -+CONFIG_ARCH_HAS_RELR=y -+CONFIG_ARCH_WANT_LD_ORPHAN_WARN=y -+ -+# -+# GCOV-based kernel profiling -+# -+# CONFIG_GCOV_KERNEL is not set -+CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y -+# end of GCOV-based kernel profiling -+ -+CONFIG_HAVE_GCC_PLUGINS=y -+CONFIG_GCC_PLUGINS=y -+# CONFIG_GCC_PLUGIN_CYC_COMPLEXITY is not set -+# CONFIG_GCC_PLUGIN_LATENT_ENTROPY is not set -+# CONFIG_GCC_PLUGIN_RANDSTRUCT is not set -+# end of General architecture-dependent options -+ -+CONFIG_RT_MUTEXES=y -+CONFIG_BASE_SMALL=0 -+CONFIG_MODULE_SIG_FORMAT=y - CONFIG_MODULES=y -+# CONFIG_MODULE_FORCE_LOAD is not set - CONFIG_MODULE_UNLOAD=y -+# CONFIG_MODULE_FORCE_UNLOAD is not set - CONFIG_MODVERSIONS=y -+CONFIG_ASM_MODVERSIONS=y -+# CONFIG_MODULE_SRCVERSION_ALL is not set - CONFIG_MODULE_SIG=y -+# CONFIG_MODULE_SIG_FORCE is not set -+CONFIG_MODULE_SIG_ALL=y -+# CONFIG_MODULE_SIG_SHA1 is not set -+# CONFIG_MODULE_SIG_SHA224 is not set -+# CONFIG_MODULE_SIG_SHA256 is not set -+# CONFIG_MODULE_SIG_SHA384 is not set - CONFIG_MODULE_SIG_SHA512=y -+CONFIG_MODULE_SIG_HASH="sha512" -+# CONFIG_MODULE_COMPRESS is not set -+# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set -+# CONFIG_UNUSED_SYMBOLS is not set -+# CONFIG_TRIM_UNUSED_KSYMS is not set -+CONFIG_MODULES_TREE_LOOKUP=y -+CONFIG_BLOCK=y -+CONFIG_BLK_SCSI_REQUEST=y -+CONFIG_BLK_CGROUP_RWSTAT=y -+CONFIG_BLK_DEV_BSG=y -+CONFIG_BLK_DEV_BSGLIB=y -+CONFIG_BLK_DEV_INTEGRITY=y -+CONFIG_BLK_DEV_INTEGRITY_T10=y -+# CONFIG_BLK_DEV_ZONED is not set - CONFIG_BLK_DEV_THROTTLING=y -+# CONFIG_BLK_DEV_THROTTLING_LOW is not set -+# CONFIG_BLK_CMDLINE_PARSER is not set -+# CONFIG_BLK_WBT is not set -+# CONFIG_BLK_CGROUP_IOLATENCY is not set -+# CONFIG_BLK_CGROUP_IOCOST is not set -+CONFIG_BLK_DEBUG_FS=y -+# CONFIG_BLK_SED_OPAL is not set -+# CONFIG_BLK_INLINE_ENCRYPTION is not set -+ -+# -+# Partition Types -+# - CONFIG_PARTITION_ADVANCED=y -+# CONFIG_ACORN_PARTITION is not set -+# CONFIG_AIX_PARTITION is not set -+# CONFIG_OSF_PARTITION is not set -+# CONFIG_AMIGA_PARTITION is not set -+# CONFIG_ATARI_PARTITION is not set -+# CONFIG_MAC_PARTITION is not set -+CONFIG_MSDOS_PARTITION=y -+# CONFIG_BSD_DISKLABEL is not set -+# CONFIG_MINIX_SUBPARTITION is not set -+# CONFIG_SOLARIS_X86_PARTITION is not set -+# CONFIG_UNIXWARE_DISKLABEL is not set -+# CONFIG_LDM_PARTITION is not set -+# CONFIG_SGI_PARTITION is not set -+# CONFIG_ULTRIX_PARTITION is not set -+# CONFIG_SUN_PARTITION is not set -+# CONFIG_KARMA_PARTITION is not set -+CONFIG_EFI_PARTITION=y -+# CONFIG_SYSV68_PARTITION is not set -+# CONFIG_CMDLINE_PARTITION is not set -+# end of Partition Types -+ -+CONFIG_BLOCK_COMPAT=y -+CONFIG_BLK_MQ_PCI=y -+CONFIG_BLK_MQ_VIRTIO=y -+CONFIG_BLK_MQ_RDMA=y -+CONFIG_BLK_PM=y -+ -+# -+# IO Schedulers -+# -+CONFIG_MQ_IOSCHED_DEADLINE=y -+CONFIG_MQ_IOSCHED_KYBER=y -+# CONFIG_IOSCHED_BFQ is not set -+# end of IO Schedulers -+ -+CONFIG_PREEMPT_NOTIFIERS=y -+CONFIG_ASN1=y -+CONFIG_UNINLINE_SPIN_UNLOCK=y -+CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y -+CONFIG_MUTEX_SPIN_ON_OWNER=y -+CONFIG_RWSEM_SPIN_ON_OWNER=y -+CONFIG_LOCK_SPIN_ON_OWNER=y -+CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y -+CONFIG_QUEUED_SPINLOCKS=y -+CONFIG_ARCH_USE_QUEUED_RWLOCKS=y -+CONFIG_QUEUED_RWLOCKS=y -+CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE=y -+CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y -+CONFIG_FREEZER=y -+ -+# -+# Executable file formats -+# -+CONFIG_BINFMT_ELF=y -+CONFIG_COMPAT_BINFMT_ELF=y -+CONFIG_ARCH_BINFMT_ELF_STATE=y -+CONFIG_ARCH_HAVE_ELF_PROT=y -+CONFIG_ARCH_USE_GNU_PROPERTY=y -+CONFIG_ELFCORE=y - # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set -+CONFIG_BINFMT_SCRIPT=y - CONFIG_BINFMT_MISC=m -+CONFIG_COREDUMP=y -+# end of Executable file formats -+ -+# -+# Memory Management options -+# -+CONFIG_SELECT_MEMORY_MODEL=y -+# CONFIG_FLATMEM_MANUAL is not set -+CONFIG_SPARSEMEM_MANUAL=y -+CONFIG_SPARSEMEM=y -+CONFIG_SPARSEMEM_EXTREME=y -+CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y -+CONFIG_SPARSEMEM_VMEMMAP=y -+CONFIG_HAVE_FAST_GUP=y -+CONFIG_ARCH_KEEP_MEMBLOCK=y -+CONFIG_MEMORY_ISOLATION=y -+# CONFIG_MEMORY_HOTPLUG is not set -+CONFIG_SPLIT_PTLOCK_CPUS=4 -+CONFIG_MEMORY_BALLOON=y -+CONFIG_BALLOON_COMPACTION=y -+CONFIG_COMPACTION=y -+CONFIG_PAGE_REPORTING=y -+CONFIG_MIGRATION=y -+CONFIG_CONTIG_ALLOC=y -+CONFIG_PHYS_ADDR_T_64BIT=y -+CONFIG_BOUNCE=y -+CONFIG_MMU_NOTIFIER=y - CONFIG_KSM=y - CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 -+CONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y - CONFIG_MEMORY_FAILURE=y -+# CONFIG_HWPOISON_INJECT is not set - CONFIG_TRANSPARENT_HUGEPAGE=y -+CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y -+# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set -+# CONFIG_CLEANCACHE is not set -+# CONFIG_FRONTSWAP is not set - CONFIG_CMA=y -+# CONFIG_CMA_DEBUG is not set -+# CONFIG_CMA_DEBUGFS is not set -+CONFIG_CMA_AREAS=7 -+# CONFIG_ZPOOL is not set -+# CONFIG_ZBUD is not set - CONFIG_ZSMALLOC=y -+# CONFIG_ZSMALLOC_STAT is not set -+CONFIG_GENERIC_EARLY_IOREMAP=y -+# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set -+# CONFIG_IDLE_PAGE_TRACKING is not set -+CONFIG_ARCH_HAS_PTE_DEVMAP=y -+CONFIG_HMM_MIRROR=y -+CONFIG_FRAME_VECTOR=y -+CONFIG_ARCH_USES_HIGH_VMA_FLAGS=y -+# CONFIG_PERCPU_STATS is not set -+# CONFIG_GUP_BENCHMARK is not set -+# CONFIG_READ_ONLY_THP_FOR_FS is not set -+CONFIG_ARCH_HAS_PTE_SPECIAL=y -+# end of Memory Management options -+ - CONFIG_NET=y -+CONFIG_COMPAT_NETLINK_MESSAGES=y -+CONFIG_NET_INGRESS=y -+CONFIG_NET_EGRESS=y -+CONFIG_SKB_EXTENSIONS=y -+ -+# -+# Networking options -+# - CONFIG_PACKET=y -+# CONFIG_PACKET_DIAG is not set - CONFIG_UNIX=y -+CONFIG_UNIX_SCM=y -+# CONFIG_UNIX_DIAG is not set -+# CONFIG_TLS is not set -+CONFIG_XFRM=y -+CONFIG_XFRM_ALGO=y - CONFIG_XFRM_USER=y -+# CONFIG_XFRM_INTERFACE is not set -+# CONFIG_XFRM_SUB_POLICY is not set -+# CONFIG_XFRM_MIGRATE is not set -+# CONFIG_XFRM_STATISTICS is not set -+CONFIG_XFRM_AH=m -+CONFIG_XFRM_ESP=m -+CONFIG_XFRM_IPCOMP=m - CONFIG_NET_KEY=y -+# CONFIG_NET_KEY_MIGRATE is not set -+# CONFIG_SMC is not set -+# CONFIG_XDP_SOCKETS is not set - CONFIG_INET=y - CONFIG_IP_MULTICAST=y - CONFIG_IP_ADVANCED_ROUTER=y -+# CONFIG_IP_FIB_TRIE_STATS is not set - CONFIG_IP_MULTIPLE_TABLES=y -+# CONFIG_IP_ROUTE_MULTIPATH is not set -+# CONFIG_IP_ROUTE_VERBOSE is not set - CONFIG_IP_PNP=y - CONFIG_IP_PNP_DHCP=y - CONFIG_IP_PNP_BOOTP=y -+# CONFIG_IP_PNP_RARP is not set -+# CONFIG_NET_IPIP is not set - CONFIG_NET_IPGRE_DEMUX=m -+CONFIG_NET_IP_TUNNEL=y -+# CONFIG_NET_IPGRE is not set -+# CONFIG_IP_MROUTE is not set - CONFIG_SYN_COOKIES=y -+# CONFIG_NET_IPVTI is not set -+CONFIG_NET_UDP_TUNNEL=y -+# CONFIG_NET_FOU is not set -+# CONFIG_NET_FOU_IP_TUNNELS is not set -+# CONFIG_INET_AH is not set - CONFIG_INET_ESP=m -+# CONFIG_INET_ESP_OFFLOAD is not set -+# CONFIG_INET_ESPINTCP is not set -+# CONFIG_INET_IPCOMP is not set -+CONFIG_INET_TUNNEL=m - CONFIG_INET_DIAG=m -+CONFIG_INET_TCP_DIAG=m -+# CONFIG_INET_UDP_DIAG is not set -+# CONFIG_INET_RAW_DIAG is not set -+# CONFIG_INET_DIAG_DESTROY is not set -+# CONFIG_TCP_CONG_ADVANCED is not set -+CONFIG_TCP_CONG_CUBIC=y -+CONFIG_DEFAULT_TCP_CONG="cubic" -+# CONFIG_TCP_MD5SIG is not set -+CONFIG_IPV6=y - CONFIG_IPV6_ROUTER_PREF=y - CONFIG_IPV6_ROUTE_INFO=y - CONFIG_IPV6_OPTIMISTIC_DAD=y - CONFIG_INET6_AH=m - CONFIG_INET6_ESP=m -+# CONFIG_INET6_ESP_OFFLOAD is not set -+# CONFIG_INET6_ESPINTCP is not set - CONFIG_INET6_IPCOMP=m - CONFIG_IPV6_MIP6=m -+# CONFIG_IPV6_ILA is not set -+CONFIG_INET6_XFRM_TUNNEL=m -+CONFIG_INET6_TUNNEL=m -+# CONFIG_IPV6_VTI is not set - CONFIG_IPV6_SIT=m -+# CONFIG_IPV6_SIT_6RD is not set -+CONFIG_IPV6_NDISC_NODETYPE=y - CONFIG_IPV6_TUNNEL=m -+# CONFIG_IPV6_GRE is not set - CONFIG_IPV6_MULTIPLE_TABLES=y -+# CONFIG_IPV6_SUBTREES is not set -+# CONFIG_IPV6_MROUTE is not set -+# CONFIG_IPV6_SEG6_LWTUNNEL is not set -+# CONFIG_IPV6_SEG6_HMAC is not set -+# CONFIG_IPV6_RPL_LWTUNNEL is not set -+# CONFIG_NETLABEL is not set -+# CONFIG_MPTCP is not set -+CONFIG_NETWORK_SECMARK=y -+CONFIG_NET_PTP_CLASSIFY=y -+# CONFIG_NETWORK_PHY_TIMESTAMPING is not set - CONFIG_NETFILTER=y -+CONFIG_NETFILTER_ADVANCED=y - CONFIG_BRIDGE_NETFILTER=m -+ -+# -+# Core Netfilter Configuration -+# -+CONFIG_NETFILTER_INGRESS=y -+CONFIG_NETFILTER_NETLINK=m -+CONFIG_NETFILTER_FAMILY_BRIDGE=y -+CONFIG_NETFILTER_FAMILY_ARP=y - CONFIG_NETFILTER_NETLINK_ACCT=m - CONFIG_NETFILTER_NETLINK_QUEUE=m - CONFIG_NETFILTER_NETLINK_LOG=m -+# CONFIG_NETFILTER_NETLINK_OSF is not set - CONFIG_NF_CONNTRACK=m -+CONFIG_NF_LOG_COMMON=m -+# CONFIG_NF_LOG_NETDEV is not set -+CONFIG_NETFILTER_CONNCOUNT=m -+CONFIG_NF_CONNTRACK_MARK=y -+# CONFIG_NF_CONNTRACK_SECMARK is not set -+# CONFIG_NF_CONNTRACK_ZONES is not set -+CONFIG_NF_CONNTRACK_PROCFS=y - CONFIG_NF_CONNTRACK_EVENTS=y -+# CONFIG_NF_CONNTRACK_TIMEOUT is not set -+# CONFIG_NF_CONNTRACK_TIMESTAMP is not set -+# CONFIG_NF_CONNTRACK_LABELS is not set -+CONFIG_NF_CT_PROTO_DCCP=y -+CONFIG_NF_CT_PROTO_GRE=y -+CONFIG_NF_CT_PROTO_SCTP=y -+CONFIG_NF_CT_PROTO_UDPLITE=y - CONFIG_NF_CONNTRACK_AMANDA=m - CONFIG_NF_CONNTRACK_FTP=m - CONFIG_NF_CONNTRACK_H323=m - CONFIG_NF_CONNTRACK_IRC=m -+CONFIG_NF_CONNTRACK_BROADCAST=m - CONFIG_NF_CONNTRACK_NETBIOS_NS=m -+# CONFIG_NF_CONNTRACK_SNMP is not set - CONFIG_NF_CONNTRACK_PPTP=m - CONFIG_NF_CONNTRACK_SANE=m - CONFIG_NF_CONNTRACK_SIP=m - CONFIG_NF_CONNTRACK_TFTP=m - CONFIG_NF_CT_NETLINK=m -+# CONFIG_NETFILTER_NETLINK_GLUE_CT is not set -+CONFIG_NF_NAT=m -+CONFIG_NF_NAT_AMANDA=m -+CONFIG_NF_NAT_FTP=m -+CONFIG_NF_NAT_IRC=m -+CONFIG_NF_NAT_SIP=m -+CONFIG_NF_NAT_TFTP=m -+CONFIG_NF_NAT_REDIRECT=y -+CONFIG_NF_NAT_MASQUERADE=y -+# CONFIG_NF_TABLES is not set -+CONFIG_NETFILTER_XTABLES=m -+ -+# -+# Xtables combined modules -+# -+CONFIG_NETFILTER_XT_MARK=m -+CONFIG_NETFILTER_XT_CONNMARK=m -+ -+# -+# Xtables targets -+# -+# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set - CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m - CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m - CONFIG_NETFILTER_XT_TARGET_CONNMARK=m -+# CONFIG_NETFILTER_XT_TARGET_CT is not set -+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set -+# CONFIG_NETFILTER_XT_TARGET_HL is not set -+# CONFIG_NETFILTER_XT_TARGET_HMARK is not set - CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m -+# CONFIG_NETFILTER_XT_TARGET_LED is not set - CONFIG_NETFILTER_XT_TARGET_LOG=m - CONFIG_NETFILTER_XT_TARGET_MARK=m -+CONFIG_NETFILTER_XT_NAT=m -+CONFIG_NETFILTER_XT_TARGET_NETMAP=m -+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set -+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set -+# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set -+# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set -+CONFIG_NETFILTER_XT_TARGET_REDIRECT=m -+CONFIG_NETFILTER_XT_TARGET_MASQUERADE=m - CONFIG_NETFILTER_XT_TARGET_TEE=m - CONFIG_NETFILTER_XT_TARGET_TPROXY=m - CONFIG_NETFILTER_XT_TARGET_TRACE=m -+# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set - CONFIG_NETFILTER_XT_TARGET_TCPMSS=m -+# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set -+ -+# -+# Xtables matches -+# - CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m -+# CONFIG_NETFILTER_XT_MATCH_BPF is not set -+# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set -+# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set - CONFIG_NETFILTER_XT_MATCH_COMMENT=m - CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m -+# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set - CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m - CONFIG_NETFILTER_XT_MATCH_CONNMARK=m - CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m -+# CONFIG_NETFILTER_XT_MATCH_CPU is not set -+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set -+# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set -+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set -+CONFIG_NETFILTER_XT_MATCH_ECN=m -+# CONFIG_NETFILTER_XT_MATCH_ESP is not set - CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m - CONFIG_NETFILTER_XT_MATCH_HELPER=m -+CONFIG_NETFILTER_XT_MATCH_HL=m -+# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set - CONFIG_NETFILTER_XT_MATCH_IPRANGE=m - CONFIG_NETFILTER_XT_MATCH_IPVS=m -+# CONFIG_NETFILTER_XT_MATCH_L2TP is not set - CONFIG_NETFILTER_XT_MATCH_LENGTH=m - CONFIG_NETFILTER_XT_MATCH_LIMIT=m - CONFIG_NETFILTER_XT_MATCH_MAC=m - CONFIG_NETFILTER_XT_MATCH_MARK=m - CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m -+# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set -+# CONFIG_NETFILTER_XT_MATCH_OSF is not set - CONFIG_NETFILTER_XT_MATCH_OWNER=m -+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set -+# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set - CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m - CONFIG_NETFILTER_XT_MATCH_QUOTA=m -+# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set -+# CONFIG_NETFILTER_XT_MATCH_REALM is not set - CONFIG_NETFILTER_XT_MATCH_RECENT=m -+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set - CONFIG_NETFILTER_XT_MATCH_SOCKET=m - CONFIG_NETFILTER_XT_MATCH_STATE=m - CONFIG_NETFILTER_XT_MATCH_STATISTIC=m - CONFIG_NETFILTER_XT_MATCH_STRING=m -+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set - CONFIG_NETFILTER_XT_MATCH_TIME=m - CONFIG_NETFILTER_XT_MATCH_U32=m -+# end of Core Netfilter Configuration -+ -+# CONFIG_IP_SET is not set - CONFIG_IP_VS=m -+# CONFIG_IP_VS_IPV6 is not set -+# CONFIG_IP_VS_DEBUG is not set -+CONFIG_IP_VS_TAB_BITS=12 -+ -+# -+# IPVS transport protocol load balancing support -+# - CONFIG_IP_VS_PROTO_TCP=y - CONFIG_IP_VS_PROTO_UDP=y -+# CONFIG_IP_VS_PROTO_ESP is not set -+# CONFIG_IP_VS_PROTO_AH is not set -+# CONFIG_IP_VS_PROTO_SCTP is not set -+ -+# -+# IPVS scheduler -+# - CONFIG_IP_VS_RR=m -+# CONFIG_IP_VS_WRR is not set -+# CONFIG_IP_VS_LC is not set -+# CONFIG_IP_VS_WLC is not set -+# CONFIG_IP_VS_FO is not set -+# CONFIG_IP_VS_OVF is not set -+# CONFIG_IP_VS_LBLC is not set -+# CONFIG_IP_VS_LBLCR is not set -+# CONFIG_IP_VS_DH is not set -+# CONFIG_IP_VS_SH is not set -+# CONFIG_IP_VS_MH is not set -+# CONFIG_IP_VS_SED is not set -+# CONFIG_IP_VS_NQ is not set -+ -+# -+# IPVS SH scheduler -+# -+CONFIG_IP_VS_SH_TAB_BITS=8 -+ -+# -+# IPVS MH scheduler -+# -+CONFIG_IP_VS_MH_TAB_INDEX=12 -+ -+# -+# IPVS application helper -+# -+# CONFIG_IP_VS_FTP is not set - CONFIG_IP_VS_NFCT=y -+# CONFIG_IP_VS_PE_SIP is not set -+ -+# -+# IP: Netfilter Configuration -+# -+CONFIG_NF_DEFRAG_IPV4=m -+CONFIG_NF_SOCKET_IPV4=m -+CONFIG_NF_TPROXY_IPV4=m -+CONFIG_NF_DUP_IPV4=m -+# CONFIG_NF_LOG_ARP is not set -+CONFIG_NF_LOG_IPV4=m -+CONFIG_NF_REJECT_IPV4=m -+CONFIG_NF_NAT_PPTP=m -+CONFIG_NF_NAT_H323=m - CONFIG_IP_NF_IPTABLES=m - CONFIG_IP_NF_MATCH_AH=m - CONFIG_IP_NF_MATCH_ECN=m -@@ -203,89 +1285,326 @@ CONFIG_IP_NF_MATCH_RPFILTER=m - CONFIG_IP_NF_MATCH_TTL=m - CONFIG_IP_NF_FILTER=m - CONFIG_IP_NF_TARGET_REJECT=m -+# CONFIG_IP_NF_TARGET_SYNPROXY is not set - CONFIG_IP_NF_NAT=m - CONFIG_IP_NF_TARGET_MASQUERADE=m - CONFIG_IP_NF_TARGET_NETMAP=m - CONFIG_IP_NF_TARGET_REDIRECT=m - CONFIG_IP_NF_MANGLE=m -+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set -+# CONFIG_IP_NF_TARGET_ECN is not set -+# CONFIG_IP_NF_TARGET_TTL is not set - CONFIG_IP_NF_RAW=m - CONFIG_IP_NF_SECURITY=m - CONFIG_IP_NF_ARPTABLES=m - CONFIG_IP_NF_ARPFILTER=m - CONFIG_IP_NF_ARP_MANGLE=m -+# end of IP: Netfilter Configuration -+ -+# -+# IPv6: Netfilter Configuration -+# -+CONFIG_NF_SOCKET_IPV6=m -+CONFIG_NF_TPROXY_IPV6=m -+CONFIG_NF_DUP_IPV6=m -+CONFIG_NF_REJECT_IPV6=m -+CONFIG_NF_LOG_IPV6=m - CONFIG_IP6_NF_IPTABLES=m -+# CONFIG_IP6_NF_MATCH_AH is not set -+# CONFIG_IP6_NF_MATCH_EUI64 is not set -+# CONFIG_IP6_NF_MATCH_FRAG is not set -+# CONFIG_IP6_NF_MATCH_OPTS is not set -+# CONFIG_IP6_NF_MATCH_HL is not set -+# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set -+# CONFIG_IP6_NF_MATCH_MH is not set -+# CONFIG_IP6_NF_MATCH_RPFILTER is not set -+# CONFIG_IP6_NF_MATCH_RT is not set -+# CONFIG_IP6_NF_MATCH_SRH is not set -+# CONFIG_IP6_NF_TARGET_HL is not set - CONFIG_IP6_NF_FILTER=m - CONFIG_IP6_NF_TARGET_REJECT=m -+# CONFIG_IP6_NF_TARGET_SYNPROXY is not set - CONFIG_IP6_NF_MANGLE=m - CONFIG_IP6_NF_RAW=m -+# CONFIG_IP6_NF_SECURITY is not set - CONFIG_IP6_NF_NAT=m - CONFIG_IP6_NF_TARGET_MASQUERADE=m -+# CONFIG_IP6_NF_TARGET_NPT is not set -+# end of IPv6: Netfilter Configuration -+ -+CONFIG_NF_DEFRAG_IPV6=m -+# CONFIG_NF_CONNTRACK_BRIDGE is not set -+# CONFIG_BRIDGE_NF_EBTABLES is not set -+# CONFIG_BPFILTER is not set -+# CONFIG_IP_DCCP is not set -+# CONFIG_IP_SCTP is not set -+# CONFIG_RDS is not set -+# CONFIG_TIPC is not set -+# CONFIG_ATM is not set -+# CONFIG_L2TP is not set -+CONFIG_STP=y -+CONFIG_GARP=m -+CONFIG_MRP=m - CONFIG_BRIDGE=y -+CONFIG_BRIDGE_IGMP_SNOOPING=y - CONFIG_BRIDGE_VLAN_FILTERING=y -+# CONFIG_BRIDGE_MRP is not set -+CONFIG_HAVE_NET_DSA=y - CONFIG_NET_DSA=m -+# CONFIG_NET_DSA_TAG_AR9331 is not set -+# CONFIG_NET_DSA_TAG_BRCM is not set -+# CONFIG_NET_DSA_TAG_BRCM_PREPEND is not set -+# CONFIG_NET_DSA_TAG_GSWIP is not set -+# CONFIG_NET_DSA_TAG_DSA is not set -+# CONFIG_NET_DSA_TAG_EDSA is not set -+# CONFIG_NET_DSA_TAG_MTK is not set -+# CONFIG_NET_DSA_TAG_KSZ is not set -+# CONFIG_NET_DSA_TAG_RTL4_A is not set - CONFIG_NET_DSA_TAG_OCELOT=m -+# CONFIG_NET_DSA_TAG_QCA is not set -+# CONFIG_NET_DSA_TAG_LAN9303 is not set -+# CONFIG_NET_DSA_TAG_SJA1105 is not set -+# CONFIG_NET_DSA_TAG_TRAILER is not set - CONFIG_VLAN_8021Q=m - CONFIG_VLAN_8021Q_GVRP=y - CONFIG_VLAN_8021Q_MVRP=y -+# CONFIG_DECNET is not set -+CONFIG_LLC=y -+# CONFIG_LLC2 is not set -+# CONFIG_ATALK is not set -+# CONFIG_X25 is not set -+# CONFIG_LAPB is not set -+# CONFIG_PHONET is not set -+# CONFIG_6LOWPAN is not set -+# CONFIG_IEEE802154 is not set - CONFIG_NET_SCHED=y -+ -+# -+# Queueing/Scheduling -+# -+# CONFIG_NET_SCH_CBQ is not set - CONFIG_NET_SCH_HTB=y -+# CONFIG_NET_SCH_HFSC is not set -+# CONFIG_NET_SCH_PRIO is not set -+# CONFIG_NET_SCH_MULTIQ is not set -+# CONFIG_NET_SCH_RED is not set -+# CONFIG_NET_SCH_SFB is not set -+# CONFIG_NET_SCH_SFQ is not set -+# CONFIG_NET_SCH_TEQL is not set -+# CONFIG_NET_SCH_TBF is not set - CONFIG_NET_SCH_CBS=y - CONFIG_NET_SCH_ETF=m - CONFIG_NET_SCH_TAPRIO=m -+# CONFIG_NET_SCH_GRED is not set -+# CONFIG_NET_SCH_DSMARK is not set -+# CONFIG_NET_SCH_NETEM is not set -+# CONFIG_NET_SCH_DRR is not set - CONFIG_NET_SCH_MQPRIO=m -+# CONFIG_NET_SCH_SKBPRIO is not set -+# CONFIG_NET_SCH_CHOKE is not set -+# CONFIG_NET_SCH_QFQ is not set -+# CONFIG_NET_SCH_CODEL is not set -+# CONFIG_NET_SCH_FQ_CODEL is not set -+# CONFIG_NET_SCH_CAKE is not set -+# CONFIG_NET_SCH_FQ is not set -+# CONFIG_NET_SCH_HHF is not set -+# CONFIG_NET_SCH_PIE is not set - CONFIG_NET_SCH_INGRESS=m -+# CONFIG_NET_SCH_PLUG is not set -+# CONFIG_NET_SCH_ETS is not set -+# CONFIG_NET_SCH_DEFAULT is not set -+ -+# -+# Classification -+# -+CONFIG_NET_CLS=y - CONFIG_NET_CLS_BASIC=m -+# CONFIG_NET_CLS_TCINDEX is not set -+# CONFIG_NET_CLS_ROUTE4 is not set -+# CONFIG_NET_CLS_FW is not set - CONFIG_NET_CLS_U32=y -+# CONFIG_CLS_U32_PERF is not set -+# CONFIG_CLS_U32_MARK is not set -+# CONFIG_NET_CLS_RSVP is not set -+# CONFIG_NET_CLS_RSVP6 is not set -+# CONFIG_NET_CLS_FLOW is not set - CONFIG_NET_CLS_CGROUP=y -+# CONFIG_NET_CLS_BPF is not set - CONFIG_NET_CLS_FLOWER=m -+# CONFIG_NET_CLS_MATCHALL is not set - CONFIG_NET_EMATCH=y -+CONFIG_NET_EMATCH_STACK=32 -+# CONFIG_NET_EMATCH_CMP is not set -+# CONFIG_NET_EMATCH_NBYTE is not set - CONFIG_NET_EMATCH_U32=y -+# CONFIG_NET_EMATCH_META is not set -+# CONFIG_NET_EMATCH_TEXT is not set -+# CONFIG_NET_EMATCH_CANID is not set -+# CONFIG_NET_EMATCH_IPT is not set - CONFIG_NET_CLS_ACT=y - CONFIG_NET_ACT_POLICE=m - CONFIG_NET_ACT_GACT=m -+# CONFIG_GACT_PROB is not set - CONFIG_NET_ACT_MIRRED=m -+# CONFIG_NET_ACT_SAMPLE is not set -+# CONFIG_NET_ACT_IPT is not set -+# CONFIG_NET_ACT_NAT is not set -+# CONFIG_NET_ACT_PEDIT is not set -+# CONFIG_NET_ACT_SIMP is not set -+# CONFIG_NET_ACT_SKBEDIT is not set -+# CONFIG_NET_ACT_CSUM is not set -+# CONFIG_NET_ACT_MPLS is not set -+# CONFIG_NET_ACT_VLAN is not set -+# CONFIG_NET_ACT_BPF is not set -+# CONFIG_NET_ACT_CONNMARK is not set -+# CONFIG_NET_ACT_CTINFO is not set -+# CONFIG_NET_ACT_SKBMOD is not set -+# CONFIG_NET_ACT_IFE is not set -+# CONFIG_NET_ACT_TUNNEL_KEY is not set - CONFIG_NET_ACT_GATE=m -+# CONFIG_NET_TC_SKB_EXT is not set -+CONFIG_NET_SCH_FIFO=y -+# CONFIG_DCB is not set -+CONFIG_DNS_RESOLVER=y -+# CONFIG_BATMAN_ADV is not set -+# CONFIG_OPENVSWITCH is not set -+# CONFIG_VSOCKETS is not set -+# CONFIG_NETLINK_DIAG is not set -+# CONFIG_MPLS is not set -+# CONFIG_NET_NSH is not set -+# CONFIG_HSR is not set -+CONFIG_NET_SWITCHDEV=y -+CONFIG_NET_L3_MASTER_DEV=y -+# CONFIG_QRTR is not set -+# CONFIG_NET_NCSI is not set -+CONFIG_RPS=y -+CONFIG_RFS_ACCEL=y -+CONFIG_XPS=y - CONFIG_CGROUP_NET_PRIO=y -+CONFIG_CGROUP_NET_CLASSID=y -+CONFIG_NET_RX_BUSY_POLL=y -+CONFIG_BQL=y - CONFIG_BPF_JIT=y -+CONFIG_NET_FLOW_LIMIT=y -+ -+# -+# Network testing -+# -+# CONFIG_NET_PKTGEN is not set -+# CONFIG_NET_DROP_MONITOR is not set -+# end of Network testing -+# end of Networking options -+ -+# CONFIG_HAMRADIO is not set - CONFIG_CAN=m -+CONFIG_CAN_RAW=m -+CONFIG_CAN_BCM=m -+CONFIG_CAN_GW=m -+# CONFIG_CAN_J1939 is not set -+# CONFIG_CAN_ISOTP is not set -+ -+# -+# CAN Device Drivers -+# - CONFIG_CAN_VCAN=m -+# CONFIG_CAN_VXCAN is not set - CONFIG_CAN_SLCAN=m -+CONFIG_CAN_DEV=m -+CONFIG_CAN_CALC_BITTIMING=y -+# CONFIG_CAN_FLEXCAN is not set -+# CONFIG_CAN_GRCAN is not set -+# CONFIG_CAN_KVASER_PCIEFD is not set -+# CONFIG_CAN_XILINXCAN is not set - CONFIG_CAN_C_CAN=m -+# CONFIG_CAN_C_CAN_PLATFORM is not set -+# CONFIG_CAN_C_CAN_PCI is not set - CONFIG_CAN_CC770=m - CONFIG_CAN_CC770_ISA=m - CONFIG_CAN_CC770_PLATFORM=m -+# CONFIG_CAN_IFI_CANFD is not set - CONFIG_CAN_M_CAN=m -+# CONFIG_CAN_M_CAN_PLATFORM is not set -+# CONFIG_CAN_M_CAN_TCAN4X5X is not set -+# CONFIG_CAN_PEAK_PCIEFD is not set - CONFIG_CAN_SJA1000=m - CONFIG_CAN_EMS_PCI=m -+# CONFIG_CAN_F81601 is not set - CONFIG_CAN_KVASER_PCI=m -+# CONFIG_CAN_PEAK_PCI is not set - CONFIG_CAN_PLX_PCI=m - CONFIG_CAN_SJA1000_ISA=m - CONFIG_CAN_SJA1000_PLATFORM=m - CONFIG_CAN_SOFTING=m -+ -+# -+# CAN SPI interfaces -+# -+# CONFIG_CAN_HI311X is not set - CONFIG_CAN_MCP251X=m -+# CONFIG_CAN_MCP251XFD is not set -+# end of CAN SPI interfaces -+ -+# -+# CAN USB interfaces -+# - CONFIG_CAN_8DEV_USB=m - CONFIG_CAN_EMS_USB=m - CONFIG_CAN_ESD_USB2=m - CONFIG_CAN_GS_USB=m - CONFIG_CAN_KVASER_USB=m -+# CONFIG_CAN_MCBA_USB is not set - CONFIG_CAN_PEAK_USB=m -+# CONFIG_CAN_UCAN is not set -+# end of CAN USB interfaces -+ -+# CONFIG_CAN_DEBUG_DEVICES is not set -+# end of CAN Device Drivers -+ - CONFIG_MTTCAN=m - CONFIG_TEGRA_HV_SECCAN=m - CONFIG_BT=y -+CONFIG_BT_BREDR=y - CONFIG_BT_RFCOMM=y -+# CONFIG_BT_RFCOMM_TTY is not set - CONFIG_BT_BNEP=m -+# CONFIG_BT_BNEP_MC_FILTER is not set -+# CONFIG_BT_BNEP_PROTO_FILTER is not set - CONFIG_BT_HIDP=y -+# CONFIG_BT_HS is not set - # CONFIG_BT_LE is not set - CONFIG_BT_LEDS=y -+# CONFIG_BT_MSFTEXT is not set - # CONFIG_BT_DEBUGFS is not set -+# CONFIG_BT_SELFTEST is not set -+# CONFIG_BT_FEATURE_DEBUG is not set -+ -+# -+# Bluetooth device drivers -+# -+CONFIG_BT_INTEL=m -+CONFIG_BT_BCM=m -+CONFIG_BT_RTL=m -+CONFIG_BT_QCA=m - CONFIG_BT_HCIBTUSB=m -+# CONFIG_BT_HCIBTUSB_AUTOSUSPEND is not set -+CONFIG_BT_HCIBTUSB_BCM=y -+# CONFIG_BT_HCIBTUSB_MTK is not set -+CONFIG_BT_HCIBTUSB_RTL=y -+# CONFIG_BT_HCIBTSDIO is not set - CONFIG_BT_HCIUART=m -+CONFIG_BT_HCIUART_SERDEV=y -+CONFIG_BT_HCIUART_H4=y -+# CONFIG_BT_HCIUART_NOKIA is not set - CONFIG_BT_HCIUART_BCSP=y - CONFIG_BT_HCIUART_ATH3K=y - CONFIG_BT_HCIUART_LL=y -+# CONFIG_BT_HCIUART_3WIRE is not set - CONFIG_BT_HCIUART_INTEL=y - CONFIG_BT_HCIUART_BCM=y -+# CONFIG_BT_HCIUART_RTL is not set - CONFIG_BT_HCIUART_QCA=y -+# CONFIG_BT_HCIUART_AG6XX is not set -+# CONFIG_BT_HCIUART_MRVL is not set - CONFIG_BT_HCIBCM203X=m - CONFIG_BT_HCIBPA10X=m - CONFIG_BT_HCIBFUSB=m -@@ -293,222 +1612,1218 @@ CONFIG_BT_HCIVHCI=m - CONFIG_BT_MRVL=m - CONFIG_BT_MRVL_SDIO=m - CONFIG_BT_ATH3K=m -+# CONFIG_BT_MTKSDIO is not set -+# CONFIG_BT_MTKUART is not set -+# end of Bluetooth device drivers -+ - CONFIG_AF_RXRPC=m -+# CONFIG_AF_RXRPC_IPV6 is not set -+# CONFIG_AF_RXRPC_INJECT_LOSS is not set -+# CONFIG_AF_RXRPC_DEBUG is not set -+# CONFIG_RXKAD is not set - CONFIG_AF_KCM=m -+CONFIG_STREAM_PARSER=y -+CONFIG_FIB_RULES=y -+CONFIG_WIRELESS=y -+CONFIG_WIRELESS_EXT=y -+CONFIG_WEXT_CORE=y -+CONFIG_WEXT_PROC=y -+CONFIG_WEXT_SPY=y -+CONFIG_WEXT_PRIV=y - CONFIG_CFG80211=m -+# CONFIG_NL80211_TESTMODE is not set -+# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set - CONFIG_CFG80211_CERTIFICATION_ONUS=y - # CONFIG_CFG80211_REQUIRE_SIGNED_REGDB is not set -+# CONFIG_CFG80211_REG_CELLULAR_HINTS is not set -+# CONFIG_CFG80211_REG_RELAX_NO_IR is not set -+CONFIG_CFG80211_DEFAULT_PS=y -+# CONFIG_CFG80211_DEBUGFS is not set -+CONFIG_CFG80211_CRDA_SUPPORT=y -+CONFIG_CFG80211_WEXT=y -+CONFIG_CFG80211_WEXT_EXPORT=y -+CONFIG_LIB80211=m -+CONFIG_LIB80211_CRYPT_WEP=m -+CONFIG_LIB80211_CRYPT_CCMP=m -+CONFIG_LIB80211_CRYPT_TKIP=m -+# CONFIG_LIB80211_DEBUG is not set - CONFIG_MAC80211=m -+CONFIG_MAC80211_HAS_RC=y -+CONFIG_MAC80211_RC_MINSTREL=y -+CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y -+CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" -+# CONFIG_MAC80211_MESH is not set -+CONFIG_MAC80211_LEDS=y -+CONFIG_MAC80211_DEBUGFS=y -+# CONFIG_MAC80211_MESSAGE_TRACING is not set -+# CONFIG_MAC80211_DEBUG_MENU is not set -+CONFIG_MAC80211_STA_HASH_MAX_SIZE=0 -+# CONFIG_WIMAX is not set - CONFIG_RFKILL=y -+CONFIG_RFKILL_LEDS=y -+# CONFIG_RFKILL_INPUT is not set -+# CONFIG_RFKILL_GPIO is not set - CONFIG_NET_9P=y -+# CONFIG_NET_9P_VIRTIO is not set -+# CONFIG_NET_9P_RDMA is not set -+# CONFIG_NET_9P_DEBUG is not set -+# CONFIG_CAIF is not set -+# CONFIG_CEPH_LIB is not set - CONFIG_NFC=m -+# CONFIG_NFC_DIGITAL is not set - CONFIG_NFC_NCI=m -+# CONFIG_NFC_NCI_SPI is not set -+# CONFIG_NFC_NCI_UART is not set -+# CONFIG_NFC_HCI is not set -+ -+# -+# Near Field Communication (NFC) devices -+# -+# CONFIG_NFC_FDP is not set -+# CONFIG_NFC_PN533_USB is not set -+# CONFIG_NFC_PN533_I2C is not set -+# CONFIG_NFC_PN532_UART is not set -+# CONFIG_NFC_MRVL_USB is not set -+# CONFIG_NFC_ST_NCI_I2C is not set -+# CONFIG_NFC_ST_NCI_SPI is not set -+# CONFIG_NFC_NXP_NCI is not set -+CONFIG_NFC_S3FWRN5=m - CONFIG_NFC_S3FWRN5_I2C=m -+# end of Near Field Communication (NFC) devices -+ -+# CONFIG_PSAMPLE is not set -+# CONFIG_NET_IFE is not set -+# CONFIG_LWTUNNEL is not set -+CONFIG_DST_CACHE=y -+CONFIG_GRO_CELLS=y -+CONFIG_NET_DEVLINK=y -+CONFIG_PAGE_POOL=y -+CONFIG_FAILOVER=y -+CONFIG_ETHTOOL_NETLINK=y -+CONFIG_HAVE_EBPF_JIT=y -+ -+# -+# Device Drivers -+# -+CONFIG_ARM_AMBA=y -+CONFIG_TEGRA_AHB=y -+CONFIG_HAVE_PCI=y - CONFIG_PCI=y -+CONFIG_PCI_DOMAINS=y -+CONFIG_PCI_DOMAINS_GENERIC=y -+CONFIG_PCI_SYSCALL=y - CONFIG_PCIEPORTBUS=y -+# CONFIG_HOTPLUG_PCI_PCIE is not set - CONFIG_PCIEAER=y -+# CONFIG_PCIEAER_INJECT is not set - CONFIG_PCIE_ECRC=y -+CONFIG_PCIEASPM=y -+# CONFIG_PCIEASPM_DEFAULT is not set -+# CONFIG_PCIEASPM_POWERSAVE is not set - CONFIG_PCIEASPM_POWER_SUPERSAVE=y --CONFIG_PCI_STUB=m -+# CONFIG_PCIEASPM_PERFORMANCE is not set -+CONFIG_PCIE_PME=y -+# CONFIG_PCIE_DPC is not set -+# CONFIG_PCIE_PTM is not set -+CONFIG_PCI_MSI=y -+CONFIG_PCI_MSI_IRQ_DOMAIN=y -+CONFIG_PCI_MSI_ARCH_FALLBACKS=y -+CONFIG_PCI_QUIRKS=y -+CONFIG_PCI_DEBUG=y -+# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set -+CONFIG_PCI_STUB=y -+# CONFIG_PCI_PF_STUB is not set -+CONFIG_PCI_ATS=y -+CONFIG_PCI_ECAM=y - CONFIG_PCI_IOV=y -+# CONFIG_PCI_PRI is not set -+# CONFIG_PCI_PASID is not set -+CONFIG_PCI_LABEL=y -+# CONFIG_PCIE_BUS_TUNE_OFF is not set -+# CONFIG_PCIE_BUS_DEFAULT is not set - CONFIG_PCIE_BUS_SAFE=y -+# CONFIG_PCIE_BUS_PERFORMANCE is not set -+# CONFIG_PCIE_BUS_PEER2PEER is not set -+CONFIG_HOTPLUG_PCI=y -+CONFIG_HOTPLUG_PCI_ACPI=y -+# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set -+# CONFIG_HOTPLUG_PCI_CPCI is not set -+# CONFIG_HOTPLUG_PCI_SHPC is not set -+ -+# -+# PCI controller drivers -+# -+# CONFIG_PCI_FTPCI100 is not set - CONFIG_PCI_TEGRA=y -+CONFIG_PCI_HOST_COMMON=y -+CONFIG_PCI_HOST_GENERIC=y -+# CONFIG_PCIE_XILINX is not set -+# CONFIG_PCI_XGENE is not set -+# CONFIG_PCIE_ALTERA is not set -+# CONFIG_PCI_HOST_THUNDER_PEM is not set -+# CONFIG_PCI_HOST_THUNDER_ECAM is not set -+# CONFIG_PCIE_HISI_ERR is not set -+ -+# -+# DesignWare PCI Core Support -+# -+CONFIG_PCIE_DW=y -+CONFIG_PCIE_DW_HOST=y -+CONFIG_PCIE_DW_EP=y -+# CONFIG_PCIE_DW_PLAT_HOST is not set -+# CONFIG_PCIE_DW_PLAT_EP is not set -+# CONFIG_PCI_HISI is not set -+# CONFIG_PCIE_KIRIN is not set -+# CONFIG_PCI_MESON is not set -+CONFIG_PCIE_TEGRA194=y -+CONFIG_PCIE_TEGRA194_HOST=y - CONFIG_PCIE_TEGRA194_EP=y -+# CONFIG_PCIE_RP_DMA_TEST is not set -+# CONFIG_PCIE_AL is not set -+# end of DesignWare PCI Core Support -+ -+# -+# Mobiveil PCIe Core Support -+# -+CONFIG_PCIE_MOBIVEIL=y -+CONFIG_PCIE_MOBIVEIL_HOST=y - CONFIG_PCIE_LAYERSCAPE_GEN4=y -+# end of Mobiveil PCIe Core Support -+ -+# -+# Cadence PCIe controllers support -+# -+# CONFIG_PCIE_CADENCE_PLAT_HOST is not set -+# CONFIG_PCIE_CADENCE_PLAT_EP is not set -+# CONFIG_PCI_J721E_HOST is not set -+# CONFIG_PCI_J721E_EP is not set -+# end of Cadence PCIe controllers support -+# end of PCI controller drivers -+ - CONFIG_PCIE_TEGRA_VF=y -+ -+# -+# PCI Endpoint -+# - CONFIG_PCI_ENDPOINT=y - CONFIG_PCI_ENDPOINT_CONFIGFS=y - CONFIG_PCI_EPF_TEST=y - CONFIG_PCIE_EPF_NV_TEST=y - CONFIG_PCIE_EPF_TEGRA_VNET=y --CONFIG_PCI_SERIAL_CH384=m -+# CONFIG_PCIE_EPF_DMA_TEST is not set -+# end of PCI Endpoint -+ -+# -+# PCI switch controller drivers -+# -+CONFIG_PCI_SW_SWITCHTEC=m -+# end of PCI switch controller drivers -+ -+# -+# PCI Endpoint -+# -+# CONFIG_PCIE_TEGRA_DW_EP is not set -+# end of PCI Endpoint -+ -+# CONFIG_PCCARD is not set -+# CONFIG_RAPIDIO is not set -+ -+# -+# Generic Driver Options -+# - CONFIG_UEVENT_HELPER=y -+CONFIG_UEVENT_HELPER_PATH="" - CONFIG_DEVTMPFS=y - CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_STANDALONE=y -+CONFIG_PREVENT_FIRMWARE_BUILD=y -+ -+# -+# Firmware loader -+# -+CONFIG_FW_LOADER=y -+CONFIG_FW_LOADER_PAGED_BUF=y -+CONFIG_EXTRA_FIRMWARE="" - CONFIG_FW_LOADER_USER_HELPER=y - CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y -+# CONFIG_FW_LOADER_COMPRESS is not set -+CONFIG_FW_CACHE=y -+# end of Firmware loader -+ -+CONFIG_WANT_DEV_COREDUMP=y -+CONFIG_ALLOW_DEV_COREDUMP=y -+CONFIG_DEV_COREDUMP=y -+# CONFIG_DEBUG_DRIVER is not set -+# CONFIG_DEBUG_DEVRES is not set -+# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set -+# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set -+CONFIG_GENERIC_CPU_AUTOPROBE=y -+CONFIG_GENERIC_CPU_VULNERABILITIES=y -+CONFIG_SOC_BUS=y -+CONFIG_REGMAP=y -+CONFIG_REGMAP_I2C=y -+CONFIG_REGMAP_SPI=y -+CONFIG_REGMAP_SPMI=m -+CONFIG_REGMAP_MMIO=y -+CONFIG_REGMAP_IRQ=y -+CONFIG_DMA_SHARED_BUFFER=y -+# CONFIG_DMA_FENCE_TRACE is not set -+CONFIG_GENERIC_ARCH_TOPOLOGY=y -+# end of Generic Driver Options -+ -+# -+# Bus devices -+# - CONFIG_BRCMSTB_GISB_ARB=y -+# CONFIG_MOXTET is not set - CONFIG_SIMPLE_PM_BUS=y - CONFIG_TEGRA_ACONNECT=y -+# CONFIG_TEGRA_GMI is not set - CONFIG_VEXPRESS_CONFIG=y -+# CONFIG_MHI_BUS is not set -+# end of Bus devices -+ -+# CONFIG_CONNECTOR is not set -+# CONFIG_GNSS is not set - CONFIG_MTD=m -+# CONFIG_MTD_TESTS is not set -+ -+# -+# Partition parsers -+# -+# CONFIG_MTD_AR7_PARTS is not set - CONFIG_MTD_CMDLINE_PARTS=m -+CONFIG_MTD_OF_PARTS=m -+# CONFIG_MTD_AFS_PARTS is not set -+# CONFIG_MTD_REDBOOT_PARTS is not set -+# end of Partition parsers -+ -+# -+# User Modules And Translation Layers -+# -+CONFIG_MTD_BLKDEVS=m - CONFIG_MTD_BLOCK=m -+# CONFIG_MTD_BLOCK_RO is not set -+# CONFIG_FTL is not set -+# CONFIG_NFTL is not set -+# CONFIG_INFTL is not set -+# CONFIG_RFD_FTL is not set -+# CONFIG_SSFDC is not set -+# CONFIG_SM_FTL is not set -+# CONFIG_MTD_OOPS is not set -+# CONFIG_MTD_SWAP is not set -+# CONFIG_MTD_PARTITIONED_MASTER is not set -+ -+# -+# RAM/ROM/Flash chip drivers -+# -+# CONFIG_MTD_CFI is not set -+# CONFIG_MTD_JEDECPROBE is not set -+CONFIG_MTD_MAP_BANK_WIDTH_1=y -+CONFIG_MTD_MAP_BANK_WIDTH_2=y -+CONFIG_MTD_MAP_BANK_WIDTH_4=y -+CONFIG_MTD_CFI_I1=y -+CONFIG_MTD_CFI_I2=y -+# CONFIG_MTD_RAM is not set -+# CONFIG_MTD_ROM is not set -+# CONFIG_MTD_ABSENT is not set -+# end of RAM/ROM/Flash chip drivers -+ -+# -+# Mapping drivers for chip access -+# -+# CONFIG_MTD_COMPLEX_MAPPINGS is not set -+# CONFIG_MTD_INTEL_VR_NOR is not set -+# CONFIG_MTD_PLATRAM is not set -+# end of Mapping drivers for chip access -+ -+# -+# Self-contained MTD device drivers -+# -+# CONFIG_MTD_PMC551 is not set -+# CONFIG_MTD_DATAFLASH is not set -+# CONFIG_MTD_MCHP23K256 is not set -+# CONFIG_MTD_SST25L is not set - CONFIG_MTD_QSPI_FLASH=m -+# CONFIG_MTD_SLRAM is not set -+# CONFIG_MTD_PHRAM is not set -+# CONFIG_MTD_MTDRAM is not set -+# CONFIG_MTD_BLOCK2MTD is not set -+ -+# -+# Disk-On-Chip Device Drivers -+# -+# CONFIG_MTD_DOCG3 is not set -+# end of Self-contained MTD device drivers -+ - CONFIG_MTD_TEGRA_VIRT=m -+ -+# -+# NAND -+# -+CONFIG_MTD_NAND_CORE=m -+# CONFIG_MTD_ONENAND is not set -+CONFIG_MTD_NAND_ECC_SW_HAMMING=m -+# CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC is not set - CONFIG_MTD_RAW_NAND=m -+# CONFIG_MTD_NAND_ECC_SW_BCH is not set -+ -+# -+# Raw/parallel NAND flash controllers -+# -+# CONFIG_MTD_NAND_DENALI_PCI is not set -+# CONFIG_MTD_NAND_DENALI_DT is not set -+# CONFIG_MTD_NAND_CAFE is not set -+# CONFIG_MTD_NAND_BRCMNAND is not set -+# CONFIG_MTD_NAND_MXIC is not set -+# CONFIG_MTD_NAND_TEGRA is not set -+# CONFIG_MTD_NAND_GPIO is not set -+# CONFIG_MTD_NAND_PLATFORM is not set -+# CONFIG_MTD_NAND_CADENCE is not set -+# CONFIG_MTD_NAND_ARASAN is not set -+ -+# -+# Misc -+# -+# CONFIG_MTD_NAND_NANDSIM is not set -+# CONFIG_MTD_NAND_RICOH is not set -+# CONFIG_MTD_NAND_DISKONCHIP is not set -+# CONFIG_MTD_SPI_NAND is not set -+ -+# -+# ECC engine support -+# -+CONFIG_MTD_NAND_ECC=y -+# end of ECC engine support -+# end of NAND -+ -+# -+# LPDDR & LPDDR2 PCM memory drivers -+# -+# CONFIG_MTD_LPDDR is not set -+# end of LPDDR & LPDDR2 PCM memory drivers -+ - CONFIG_MTD_SPI_NOR=m -+CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y - CONFIG_MTD_UBI=m -+CONFIG_MTD_UBI_WL_THRESHOLD=4096 -+CONFIG_MTD_UBI_BEB_LIMIT=20 -+# CONFIG_MTD_UBI_FASTMAP is not set -+# CONFIG_MTD_UBI_GLUEBI is not set -+# CONFIG_MTD_UBI_BLOCK is not set -+# CONFIG_MTD_HYPERBUS is not set -+CONFIG_DTC=y -+CONFIG_OF=y -+# CONFIG_OF_UNITTEST is not set -+CONFIG_OF_FLATTREE=y -+CONFIG_OF_EARLY_FLATTREE=y -+CONFIG_OF_KOBJ=y -+CONFIG_OF_DYNAMIC=y -+CONFIG_OF_ADDRESS=y -+CONFIG_OF_IRQ=y -+CONFIG_OF_NET=y -+CONFIG_OF_RESERVED_MEM=y -+CONFIG_OF_RESOLVE=y -+CONFIG_OF_OVERLAY=y -+# CONFIG_PARPORT is not set -+CONFIG_PNP=y - # CONFIG_PNP_DEBUG_MESSAGES is not set -+ -+# -+# Protocols -+# -+CONFIG_PNPACPI=y -+CONFIG_BLK_DEV=y -+# CONFIG_BLK_DEV_NULL_BLK is not set -+# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set - CONFIG_ZRAM=m -+# CONFIG_ZRAM_WRITEBACK is not set -+# CONFIG_ZRAM_MEMORY_TRACKING is not set -+# CONFIG_BLK_DEV_UMEM is not set - CONFIG_BLK_DEV_LOOP=m -+CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 -+# CONFIG_BLK_DEV_CRYPTOLOOP is not set -+# CONFIG_BLK_DEV_DRBD is not set - CONFIG_BLK_DEV_NBD=m -+# CONFIG_BLK_DEV_SKD is not set -+# CONFIG_BLK_DEV_SX8 is not set - CONFIG_BLK_DEV_RAM=m -+CONFIG_BLK_DEV_RAM_COUNT=16 - CONFIG_BLK_DEV_RAM_SIZE=8192 -+# CONFIG_CDROM_PKTCDVD is not set -+# CONFIG_ATA_OVER_ETH is not set - CONFIG_VIRTIO_BLK=y -+# CONFIG_BLK_DEV_RBD is not set -+# CONFIG_BLK_DEV_RSXX is not set - CONFIG_TEGRA_HV_BLKDEV=y - CONFIG_TEGRA_HV_BLKDEV_OOPS=y -+ -+# -+# NVME Support -+# -+CONFIG_NVME_CORE=y - CONFIG_BLK_DEV_NVME=y -+# CONFIG_NVME_MULTIPATH is not set -+# CONFIG_NVME_HWMON is not set -+CONFIG_NVME_FABRICS=m - CONFIG_NVME_RDMA=m - CONFIG_NVME_FC=m -+# CONFIG_NVME_TCP is not set -+# CONFIG_NVME_TARGET is not set -+# end of NVME Support -+ -+# -+# Misc devices -+# -+# CONFIG_AD525X_DPOT is not set -+# CONFIG_DUMMY_IRQ is not set -+# CONFIG_PHANTOM is not set - CONFIG_TIFM_CORE=m -+CONFIG_TIFM_7XX1=m -+# CONFIG_ICS932S401 is not set -+# CONFIG_ENCLOSURE_SERVICES is not set -+# CONFIG_HP_ILO is not set -+# CONFIG_APDS9802ALS is not set -+# CONFIG_ISL29003 is not set -+# CONFIG_ISL29020 is not set -+# CONFIG_SENSORS_TSL2550 is not set -+# CONFIG_SENSORS_BH1770 is not set -+# CONFIG_SENSORS_APDS990X is not set -+# CONFIG_HMC6352 is not set -+# CONFIG_DS1682 is not set -+# CONFIG_LATTICE_ECP3_CONFIG is not set - CONFIG_SRAM=y - CONFIG_PCI_ENDPOINT_TEST=m -+# CONFIG_XILINX_SDFEC is not set -+# CONFIG_PVPANIC is not set -+# CONFIG_HISI_HIKEY_USB is not set -+# CONFIG_C2PORT is not set -+ -+# -+# EEPROM support -+# - CONFIG_EEPROM_AT24=m - CONFIG_EEPROM_AT25=m -+# CONFIG_EEPROM_LEGACY is not set -+# CONFIG_EEPROM_MAX6875 is not set -+CONFIG_EEPROM_93CX6=m -+# CONFIG_EEPROM_93XX46 is not set -+# CONFIG_EEPROM_IDT_89HPESX is not set -+# CONFIG_EEPROM_EE1004 is not set -+# end of EEPROM support -+ - CONFIG_CB710_CORE=m -+# CONFIG_CB710_DEBUG is not set -+CONFIG_CB710_DEBUG_ASSUMPTIONS=y -+ -+# -+# Texas Instruments shared transport line discipline -+# -+# CONFIG_TI_ST is not set -+# end of Texas Instruments shared transport line discipline -+ -+# CONFIG_SENSORS_LIS3_I2C is not set -+# CONFIG_ALTERA_STAPL is not set -+# CONFIG_GENWQE is not set -+# CONFIG_ECHO is not set -+# CONFIG_MISC_ALCOR_PCI is not set -+# CONFIG_MISC_RTSX_PCI is not set -+# CONFIG_MISC_RTSX_USB is not set -+# CONFIG_HABANA_AI is not set -+# CONFIG_UACCE is not set - CONFIG_MODS=m --CONFIG_SENSORS_F75308=m -+# CONFIG_SAF775x_HWDEP is not set - CONFIG_SENSORS_NCT1008=m - CONFIG_SENSORS_PEX9749=y -+# CONFIG_TEGRA_CPC is not set -+# CONFIG_THERM_EST is not set - CONFIG_FAN_THERM_EST=y - CONFIG_EQOS_APE_HWDEP=m - CONFIG_TEGRA_ACSL=m -+# CONFIG_TEGRA_SKIN is not set - CONFIG_TEGRA_PCIE_EP_MEM=y -+# CONFIG_TEGRA_PCIE_DMA_TEST is not set -+# CONFIG_NVS is not set - CONFIG_NVS_LIGHT=m - CONFIG_NVS_PROXIMITY=y -+# CONFIG_NVS_TRIGGERED_BUFFER is not set - CONFIG_NVS_GTE=m - CONFIG_TEGRA_PROFILER=y -+CONFIG_EVENTLIB=y - CONFIG_NVSCIC2C_PCIE=m - CONFIG_NVSCIIPC=y - CONFIG_BLUEDROID_PM=m -+# end of Misc devices -+ -+# -+# SCSI device support -+# -+CONFIG_SCSI_MOD=y -+CONFIG_RAID_ATTRS=m - CONFIG_SCSI=y -+CONFIG_SCSI_DMA=y -+CONFIG_SCSI_PROC_FS=y -+ -+# -+# SCSI support type (disk, tape, CD-ROM) -+# - CONFIG_BLK_DEV_SD=y -+# CONFIG_CHR_DEV_ST is not set -+# CONFIG_BLK_DEV_SR is not set -+# CONFIG_CHR_DEV_SG is not set -+# CONFIG_CHR_DEV_SCH is not set -+# CONFIG_SCSI_CONSTANTS is not set -+# CONFIG_SCSI_LOGGING is not set -+# CONFIG_SCSI_SCAN_ASYNC is not set -+ -+# -+# SCSI Transports -+# -+# CONFIG_SCSI_SPI_ATTRS is not set -+# CONFIG_SCSI_FC_ATTRS is not set -+# CONFIG_SCSI_ISCSI_ATTRS is not set -+CONFIG_SCSI_SAS_ATTRS=m -+CONFIG_SCSI_SAS_LIBSAS=m - CONFIG_SCSI_SAS_ATA=y -+CONFIG_SCSI_SAS_HOST_SMP=y -+CONFIG_SCSI_SRP_ATTRS=m -+# end of SCSI Transports -+ -+CONFIG_SCSI_LOWLEVEL=y -+# CONFIG_ISCSI_TCP is not set -+# CONFIG_ISCSI_BOOT_SYSFS is not set -+# CONFIG_SCSI_CXGB3_ISCSI is not set -+# CONFIG_SCSI_CXGB4_ISCSI is not set -+# CONFIG_SCSI_BNX2_ISCSI is not set -+# CONFIG_BE2ISCSI is not set -+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set -+# CONFIG_SCSI_HPSA is not set -+# CONFIG_SCSI_3W_9XXX is not set -+# CONFIG_SCSI_3W_SAS is not set -+# CONFIG_SCSI_ACARD is not set -+# CONFIG_SCSI_AACRAID is not set -+# CONFIG_SCSI_AIC7XXX is not set -+# CONFIG_SCSI_AIC79XX is not set -+# CONFIG_SCSI_AIC94XX is not set - CONFIG_SCSI_HISI_SAS=m - CONFIG_SCSI_HISI_SAS_PCI=m -+# CONFIG_SCSI_MVSAS is not set -+# CONFIG_SCSI_MVUMI is not set -+# CONFIG_SCSI_ADVANSYS is not set -+# CONFIG_SCSI_ARCMSR is not set -+# CONFIG_SCSI_ESAS2R is not set -+# CONFIG_MEGARAID_NEWGEN is not set -+# CONFIG_MEGARAID_LEGACY is not set -+# CONFIG_MEGARAID_SAS is not set - CONFIG_SCSI_MPT3SAS=m -+CONFIG_SCSI_MPT2SAS_MAX_SGE=128 -+CONFIG_SCSI_MPT3SAS_MAX_SGE=128 -+# CONFIG_SCSI_MPT2SAS is not set -+# CONFIG_SCSI_SMARTPQI is not set - CONFIG_SCSI_UFSHCD=y -+# CONFIG_SCSI_UFSHCD_PCI is not set - CONFIG_SCSI_UFSHCD_PLATFORM=y -+# CONFIG_SCSI_UFS_CDNS_PLATFORM is not set -+# CONFIG_SCSI_UFS_DWC_TC_PLATFORM is not set -+# CONFIG_SCSI_UFS_BSG is not set - CONFIG_SCSI_UFSHCD_TEGRA=y -+# CONFIG_SCSI_HPTIOP is not set -+# CONFIG_SCSI_MYRB is not set -+# CONFIG_SCSI_MYRS is not set -+# CONFIG_SCSI_SNIC is not set -+# CONFIG_SCSI_DMX3191D is not set -+# CONFIG_SCSI_FDOMAIN_PCI is not set -+# CONFIG_SCSI_GDTH is not set -+# CONFIG_SCSI_IPS is not set -+# CONFIG_SCSI_INITIO is not set -+# CONFIG_SCSI_INIA100 is not set -+# CONFIG_SCSI_STEX is not set -+# CONFIG_SCSI_SYM53C8XX_2 is not set -+# CONFIG_SCSI_IPR is not set -+# CONFIG_SCSI_QLOGIC_1280 is not set -+# CONFIG_SCSI_QLA_ISCSI is not set -+# CONFIG_SCSI_DC395x is not set -+# CONFIG_SCSI_AM53C974 is not set -+# CONFIG_SCSI_WD719X is not set -+# CONFIG_SCSI_DEBUG is not set -+# CONFIG_SCSI_PMCRAID is not set -+# CONFIG_SCSI_PM8001 is not set -+# CONFIG_SCSI_VIRTIO is not set -+# CONFIG_SCSI_DH is not set -+# end of SCSI device support -+ -+CONFIG_HAVE_PATA_PLATFORM=y - CONFIG_ATA=m -+CONFIG_SATA_HOST=y -+CONFIG_ATA_VERBOSE_ERROR=y -+CONFIG_ATA_FORCE=y - # CONFIG_ATA_ACPI is not set -+CONFIG_SATA_PMP=y -+ -+# -+# Controllers with non-SFF native interface -+# - CONFIG_SATA_AHCI=m -+CONFIG_SATA_MOBILE_LPM_POLICY=0 - CONFIG_SATA_AHCI_PLATFORM=m -+# CONFIG_AHCI_CEVA is not set -+# CONFIG_AHCI_TEGRA is not set -+# CONFIG_AHCI_QORIQ is not set -+# CONFIG_SATA_INIC162X is not set -+# CONFIG_SATA_ACARD_AHCI is not set -+# CONFIG_SATA_SIL24 is not set -+CONFIG_ATA_SFF=y -+ -+# -+# SFF controllers with custom DMA interface -+# -+# CONFIG_PDC_ADMA is not set -+# CONFIG_SATA_QSTOR is not set -+# CONFIG_SATA_SX4 is not set -+CONFIG_ATA_BMDMA=y -+ -+# -+# SATA SFF controllers with BMDMA -+# -+# CONFIG_ATA_PIIX is not set -+# CONFIG_SATA_DWC is not set -+# CONFIG_SATA_MV is not set -+# CONFIG_SATA_NV is not set -+# CONFIG_SATA_PROMISE is not set -+# CONFIG_SATA_SIL is not set -+# CONFIG_SATA_SIS is not set -+# CONFIG_SATA_SVW is not set -+# CONFIG_SATA_ULI is not set -+# CONFIG_SATA_VIA is not set -+# CONFIG_SATA_VITESSE is not set -+ -+# -+# PATA SFF controllers with BMDMA -+# -+# CONFIG_PATA_ALI is not set -+# CONFIG_PATA_AMD is not set -+# CONFIG_PATA_ARTOP is not set -+# CONFIG_PATA_ATIIXP is not set -+# CONFIG_PATA_ATP867X is not set -+# CONFIG_PATA_CMD64X is not set -+# CONFIG_PATA_CYPRESS is not set -+# CONFIG_PATA_EFAR is not set -+# CONFIG_PATA_HPT366 is not set -+# CONFIG_PATA_HPT37X is not set -+# CONFIG_PATA_HPT3X2N is not set -+# CONFIG_PATA_HPT3X3 is not set -+# CONFIG_PATA_IT8213 is not set -+# CONFIG_PATA_IT821X is not set -+# CONFIG_PATA_JMICRON is not set -+# CONFIG_PATA_MARVELL is not set -+# CONFIG_PATA_NETCELL is not set -+# CONFIG_PATA_NINJA32 is not set -+# CONFIG_PATA_NS87415 is not set -+# CONFIG_PATA_OLDPIIX is not set -+# CONFIG_PATA_OPTIDMA is not set -+# CONFIG_PATA_PDC2027X is not set -+# CONFIG_PATA_PDC_OLD is not set -+# CONFIG_PATA_RADISYS is not set -+# CONFIG_PATA_RDC is not set -+# CONFIG_PATA_SCH is not set -+# CONFIG_PATA_SERVERWORKS is not set -+# CONFIG_PATA_SIL680 is not set -+# CONFIG_PATA_SIS is not set -+# CONFIG_PATA_TOSHIBA is not set -+# CONFIG_PATA_TRIFLEX is not set -+# CONFIG_PATA_VIA is not set -+# CONFIG_PATA_WINBOND is not set -+ -+# -+# PIO-only SFF controllers -+# -+# CONFIG_PATA_CMD640_PCI is not set -+# CONFIG_PATA_MPIIX is not set -+# CONFIG_PATA_NS87410 is not set -+# CONFIG_PATA_OPTI is not set -+# CONFIG_PATA_PLATFORM is not set -+# CONFIG_PATA_RZ1000 is not set -+ -+# -+# Generic fallback / legacy drivers -+# -+# CONFIG_ATA_GENERIC is not set -+# CONFIG_PATA_LEGACY is not set -+# CONFIG_AHCI_TEGRA_DOWNSTREAM is not set -+# CONFIG_SATA_AHCI_TEGRA_SHIELD is not set - CONFIG_MD=y -+# CONFIG_BLK_DEV_MD is not set -+# CONFIG_BCACHE is not set -+CONFIG_BLK_DEV_DM_BUILTIN=y - CONFIG_BLK_DEV_DM=y -+# CONFIG_DM_DEBUG is not set -+CONFIG_DM_BUFIO=y -+# CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING is not set -+# CONFIG_DM_UNSTRIPED is not set - CONFIG_DM_CRYPT=y -+# CONFIG_DM_SNAPSHOT is not set -+# CONFIG_DM_THIN_PROVISIONING is not set -+# CONFIG_DM_CACHE is not set -+# CONFIG_DM_WRITECACHE is not set -+# CONFIG_DM_EBS is not set -+# CONFIG_DM_ERA is not set -+# CONFIG_DM_CLONE is not set -+# CONFIG_DM_MIRROR is not set -+# CONFIG_DM_RAID is not set -+# CONFIG_DM_ZERO is not set -+# CONFIG_DM_MULTIPATH is not set -+# CONFIG_DM_DELAY is not set -+# CONFIG_DM_DUST is not set -+# CONFIG_DM_INIT is not set - CONFIG_DM_UEVENT=y -+# CONFIG_DM_FLAKEY is not set - CONFIG_DM_VERITY=y - CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y -+# CONFIG_DM_VERITY_FEC is not set -+# CONFIG_DM_SWITCH is not set -+# CONFIG_DM_LOG_WRITES is not set -+# CONFIG_DM_INTEGRITY is not set -+# CONFIG_TARGET_CORE is not set -+# CONFIG_FUSION is not set -+ -+# -+# IEEE 1394 (FireWire) support -+# -+# CONFIG_FIREWIRE is not set -+# CONFIG_FIREWIRE_NOSY is not set -+# end of IEEE 1394 (FireWire) support -+ - CONFIG_NETDEVICES=y -+CONFIG_MII=y -+CONFIG_NET_CORE=y -+# CONFIG_BONDING is not set - CONFIG_DUMMY=y -+# CONFIG_WIREGUARD is not set -+# CONFIG_EQUALIZER is not set -+# CONFIG_NET_FC is not set -+# CONFIG_IFB is not set -+# CONFIG_NET_TEAM is not set - CONFIG_MACVLAN=m - CONFIG_MACVTAP=m -+CONFIG_IPVLAN_L3S=y - CONFIG_IPVLAN=m -+# CONFIG_IPVTAP is not set - CONFIG_VXLAN=y -+# CONFIG_GENEVE is not set -+# CONFIG_BAREUDP is not set -+# CONFIG_GTP is not set -+# CONFIG_MACSEC is not set -+# CONFIG_NETCONSOLE is not set -+# CONFIG_NTB_NETDEV is not set - CONFIG_TUN=y -+CONFIG_TAP=m -+# CONFIG_TUN_VNET_CROSS_LE is not set - CONFIG_VETH=m - CONFIG_VIRTIO_NET=y -+# CONFIG_NLMON is not set -+# CONFIG_NET_VRF is not set -+# CONFIG_ARCNET is not set -+ -+# -+# Distributed Switch Architecture drivers -+# -+# CONFIG_B53 is not set -+# CONFIG_NET_DSA_BCM_SF2 is not set -+# CONFIG_NET_DSA_LOOP is not set -+# CONFIG_NET_DSA_LANTIQ_GSWIP is not set -+# CONFIG_NET_DSA_MT7530 is not set -+# CONFIG_NET_DSA_MV88E6060 is not set -+# CONFIG_NET_DSA_MICROCHIP_KSZ9477 is not set -+# CONFIG_NET_DSA_MICROCHIP_KSZ8795 is not set -+# CONFIG_NET_DSA_MV88E6XXX is not set -+# CONFIG_NET_DSA_MSCC_SEVILLE is not set -+# CONFIG_NET_DSA_AR9331 is not set -+# CONFIG_NET_DSA_SJA1105 is not set -+# CONFIG_NET_DSA_QCA8K is not set -+# CONFIG_NET_DSA_REALTEK_SMI is not set -+# CONFIG_NET_DSA_SMSC_LAN9303_I2C is not set -+# CONFIG_NET_DSA_SMSC_LAN9303_MDIO is not set -+# CONFIG_NET_DSA_VITESSE_VSC73XX_SPI is not set -+# CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM is not set -+# end of Distributed Switch Architecture drivers -+ -+CONFIG_ETHERNET=y -+CONFIG_MDIO=m -+CONFIG_NET_VENDOR_3COM=y -+# CONFIG_VORTEX is not set - CONFIG_TYPHOON=m -+CONFIG_NET_VENDOR_ADAPTEC=y -+# CONFIG_ADAPTEC_STARFIRE is not set -+CONFIG_NET_VENDOR_AGERE=y - CONFIG_ET131X=m -+CONFIG_NET_VENDOR_ALACRITECH=y - CONFIG_SLICOSS=m -+CONFIG_NET_VENDOR_ALTEON=y - CONFIG_ACENIC=m -+# CONFIG_ACENIC_OMIT_TIGON_I is not set - CONFIG_ALTERA_TSE=m -+CONFIG_NET_VENDOR_AMAZON=y -+# CONFIG_ENA_ETHERNET is not set -+CONFIG_NET_VENDOR_AMD=y -+# CONFIG_AMD8111_ETH is not set -+# CONFIG_PCNET32 is not set -+# CONFIG_AMD_XGBE is not set -+CONFIG_NET_VENDOR_AQUANTIA=y - CONFIG_AQTION=m -+CONFIG_NET_VENDOR_ARC=y -+CONFIG_NET_VENDOR_ATHEROS=y - CONFIG_ATL2=m - CONFIG_ATL1=m - CONFIG_ATL1E=m - CONFIG_ATL1C=m - CONFIG_ALX=m - # CONFIG_NET_VENDOR_AURORA is not set -+CONFIG_NET_VENDOR_BROADCOM=y - CONFIG_B44=m -+CONFIG_B44_PCI_AUTOSELECT=y -+CONFIG_B44_PCICORE_AUTOSELECT=y -+CONFIG_B44_PCI=y -+# CONFIG_BCMGENET is not set -+CONFIG_BNX2=m - CONFIG_CNIC=m - CONFIG_TIGON3=y -+CONFIG_TIGON3_HWMON=y - CONFIG_BNX2X=m -+CONFIG_BNX2X_SRIOV=y -+# CONFIG_SYSTEMPORT is not set - CONFIG_BNXT=m -+CONFIG_BNXT_SRIOV=y -+CONFIG_BNXT_FLOWER_OFFLOAD=y -+CONFIG_BNXT_HWMON=y -+CONFIG_NET_VENDOR_BROCADE=y - CONFIG_BNA=m -+CONFIG_NET_VENDOR_CADENCE=y - CONFIG_MACB=y -+CONFIG_MACB_USE_HWSTAMP=y -+# CONFIG_MACB_PCI is not set -+CONFIG_NET_VENDOR_CAVIUM=y - CONFIG_THUNDER_NIC_PF=m - CONFIG_THUNDER_NIC_VF=m -+CONFIG_THUNDER_NIC_BGX=m -+CONFIG_THUNDER_NIC_RGX=m - # CONFIG_CAVIUM_PTP is not set - CONFIG_LIQUIDIO=m - CONFIG_LIQUIDIO_VF=m -+CONFIG_NET_VENDOR_CHELSIO=y - CONFIG_CHELSIO_T1=m - CONFIG_CHELSIO_T1_1G=y - CONFIG_CHELSIO_T3=m - CONFIG_CHELSIO_T4=m - CONFIG_CHELSIO_T4VF=m -+CONFIG_CHELSIO_INLINE_CRYPTO=y -+CONFIG_NET_VENDOR_CISCO=y - CONFIG_ENIC=m -+CONFIG_NET_VENDOR_CORTINA=y -+# CONFIG_GEMINI_ETHERNET is not set -+# CONFIG_DNET is not set -+CONFIG_NET_VENDOR_DEC=y -+# CONFIG_NET_TULIP is not set -+CONFIG_NET_VENDOR_DLINK=y - CONFIG_DL2K=m -+# CONFIG_SUNDANCE is not set -+CONFIG_NET_VENDOR_EMULEX=y - CONFIG_BE2NET=m -+CONFIG_BE2NET_HWMON=y -+CONFIG_BE2NET_BE2=y -+CONFIG_BE2NET_BE3=y -+CONFIG_BE2NET_LANCER=y -+CONFIG_BE2NET_SKYHAWK=y -+CONFIG_NET_VENDOR_EZCHIP=y -+# CONFIG_EZCHIP_NPS_MANAGEMENT_ENET is not set -+CONFIG_NET_VENDOR_GOOGLE=y -+# CONFIG_GVE is not set -+CONFIG_NET_VENDOR_HISILICON=y -+# CONFIG_HIX5HD2_GMAC is not set -+# CONFIG_HISI_FEMAC is not set -+# CONFIG_HIP04_ETH is not set -+CONFIG_HNS_MDIO=y -+CONFIG_HNS=y - CONFIG_HNS_DSAF=y - CONFIG_HNS_ENET=y - CONFIG_HNS3=y - # CONFIG_HNS3_HCLGE is not set - CONFIG_HNS3_ENET=y -+CONFIG_NET_VENDOR_HUAWEI=y - CONFIG_HINIC=m -+CONFIG_NET_VENDOR_I825XX=y -+CONFIG_NET_VENDOR_INTEL=y - CONFIG_E100=m - CONFIG_E1000=m - CONFIG_E1000E=y - CONFIG_IGB=y -+CONFIG_IGB_HWMON=y - CONFIG_IGBVF=m - CONFIG_IXGB=m - CONFIG_IXGBE=m - # CONFIG_IXGBE_HWMON is not set - CONFIG_IXGBEVF=m - CONFIG_I40E=m -+CONFIG_IAVF=m - CONFIG_I40EVF=m - CONFIG_ICE=m - CONFIG_FM10K=m - CONFIG_IGC=m - CONFIG_JME=m -+CONFIG_NET_VENDOR_MARVELL=y -+# CONFIG_MVMDIO is not set - CONFIG_SKGE=m -+# CONFIG_SKGE_DEBUG is not set -+# CONFIG_SKGE_GENESIS is not set - CONFIG_SKY2=m - CONFIG_SKY2_DEBUG=y - CONFIG_OAK=m -+# CONFIG_OCTEONTX2_AF is not set -+# CONFIG_OCTEONTX2_PF is not set -+# CONFIG_PRESTERA is not set -+CONFIG_NET_VENDOR_MELLANOX=y - CONFIG_MLX4_EN=m -+CONFIG_MLX4_CORE=m -+CONFIG_MLX4_DEBUG=y -+CONFIG_MLX4_CORE_GEN2=y - CONFIG_MLX5_CORE=m -+# CONFIG_MLX5_FPGA is not set - CONFIG_MLX5_CORE_EN=y -+CONFIG_MLX5_EN_ARFS=y -+CONFIG_MLX5_EN_RXNFC=y -+CONFIG_MLX5_MPFS=y -+CONFIG_MLX5_ESWITCH=y -+CONFIG_MLX5_CLS_ACT=y - CONFIG_MLX5_CORE_IPOIB=y -+CONFIG_MLX5_SW_STEERING=y - CONFIG_MLX5_CORE_THERMAL=y - CONFIG_MLXSW_CORE=y -+CONFIG_MLXSW_CORE_HWMON=y -+CONFIG_MLXSW_CORE_THERMAL=y -+CONFIG_MLXSW_PCI=m -+CONFIG_MLXSW_I2C=m -+CONFIG_MLXSW_SWITCHIB=m -+CONFIG_MLXSW_SWITCHX2=m -+CONFIG_MLXSW_SPECTRUM=m -+CONFIG_MLXSW_MINIMAL=m -+CONFIG_MLXFW=y - CONFIG_MLX_MFT=m -+CONFIG_NET_VENDOR_MICREL=y -+# CONFIG_KS8842 is not set -+# CONFIG_KS8851 is not set -+# CONFIG_KS8851_MLL is not set -+# CONFIG_KSZ884X_PCI is not set -+CONFIG_NET_VENDOR_MICROCHIP=y -+# CONFIG_ENC28J60 is not set -+# CONFIG_ENCX24J600 is not set - CONFIG_LAN743X=m -+CONFIG_NET_VENDOR_MICROSEMI=y -+# CONFIG_MSCC_OCELOT_SWITCH is not set -+CONFIG_NET_VENDOR_MYRI=y -+# CONFIG_MYRI10GE is not set -+# CONFIG_FEALNX is not set -+CONFIG_NET_VENDOR_NATSEMI=y - CONFIG_NATSEMI=m - CONFIG_NS83820=m -+CONFIG_NET_VENDOR_NETERION=y - CONFIG_S2IO=m - CONFIG_VXGE=m -+# CONFIG_VXGE_DEBUG_TRACE_ALL is not set -+CONFIG_NET_VENDOR_NETRONOME=y - CONFIG_NFP=m -+CONFIG_NFP_APP_FLOWER=y -+CONFIG_NFP_APP_ABM_NIC=y - CONFIG_NFP_DEBUG=y -+CONFIG_NET_VENDOR_NI=y - CONFIG_NI_XGE_MANAGEMENT_ENET=m -+CONFIG_NET_VENDOR_8390=y - CONFIG_NE2K_PCI=m -+CONFIG_NET_VENDOR_NVIDIA=y - CONFIG_FORCEDETH=y - CONFIG_PCIE_TEGRA_VNET=y -+CONFIG_NET_VENDOR_OKI=y -+# CONFIG_ETHOC is not set -+CONFIG_NET_VENDOR_PACKET_ENGINES=y - CONFIG_HAMACHI=m - CONFIG_YELLOWFIN=m -+CONFIG_NET_VENDOR_PENSANDO=y - CONFIG_IONIC=m -+CONFIG_NET_VENDOR_QLOGIC=y - CONFIG_QLA3XXX=m - CONFIG_QLCNIC=m -+CONFIG_QLCNIC_SRIOV=y -+CONFIG_QLCNIC_HWMON=y - CONFIG_NETXEN_NIC=m -+# CONFIG_QED is not set -+CONFIG_NET_VENDOR_QUALCOMM=y -+CONFIG_QCA7000=m - CONFIG_QCA7000_SPI=m -+# CONFIG_QCA7000_UART is not set - CONFIG_QCOM_EMAC=m -+# CONFIG_RMNET is not set -+CONFIG_NET_VENDOR_RDC=y -+# CONFIG_R6040 is not set -+CONFIG_NET_VENDOR_REALTEK=y - CONFIG_8139CP=m - CONFIG_8139TOO=m -+CONFIG_8139TOO_PIO=y -+# CONFIG_8139TOO_TUNE_TWISTER is not set -+# CONFIG_8139TOO_8129 is not set -+# CONFIG_8139_OLD_RX_RESET is not set - CONFIG_R8169=m - CONFIG_R8168=m -+CONFIG_R8168_NAPI=y -+CONFIG_R8168_VLAN=y -+CONFIG_R8168_ASPM=y -+CONFIG_R8168_S5WOL=y -+CONFIG_NET_VENDOR_RENESAS=y -+CONFIG_NET_VENDOR_ROCKER=y -+# CONFIG_ROCKER is not set -+CONFIG_NET_VENDOR_SAMSUNG=y - CONFIG_SXGBE_ETH=m -+CONFIG_NET_VENDOR_SEEQ=y -+CONFIG_NET_VENDOR_SOLARFLARE=y - CONFIG_SFC=m -+CONFIG_SFC_MTD=y -+CONFIG_SFC_MCDI_MON=y -+CONFIG_SFC_SRIOV=y -+CONFIG_SFC_MCDI_LOGGING=y -+# CONFIG_SFC_FALCON is not set -+CONFIG_NET_VENDOR_SILAN=y -+# CONFIG_SC92031 is not set -+CONFIG_NET_VENDOR_SIS=y -+# CONFIG_SIS900 is not set -+# CONFIG_SIS190 is not set -+CONFIG_NET_VENDOR_SMSC=y - CONFIG_SMC91X=y -+# CONFIG_EPIC100 is not set - CONFIG_SMSC911X=y -+# CONFIG_SMSC9420 is not set - # CONFIG_NET_VENDOR_SOCIONEXT is not set -+CONFIG_NET_VENDOR_STMICRO=y - CONFIG_STMMAC_ETH=m -+# CONFIG_STMMAC_SELFTESTS is not set -+CONFIG_STMMAC_PLATFORM=m -+# CONFIG_DWMAC_DWC_QOS_ETH is not set -+CONFIG_DWMAC_GENERIC=m -+# CONFIG_DWMAC_INTEL_PLAT is not set -+# CONFIG_STMMAC_PCI is not set -+CONFIG_NET_VENDOR_SUN=y -+# CONFIG_HAPPYMEAL is not set -+# CONFIG_SUNGEM is not set -+# CONFIG_CASSINI is not set -+# CONFIG_NIU is not set -+CONFIG_NET_VENDOR_SYNOPSYS=y -+# CONFIG_DWC_XLGMAC is not set -+CONFIG_NET_VENDOR_TEHUTI=y -+# CONFIG_TEHUTI is not set -+CONFIG_NET_VENDOR_TI=y -+# CONFIG_TI_CPSW_PHY_SEL is not set -+# CONFIG_TLAN is not set -+CONFIG_NET_VENDOR_VIA=y -+# CONFIG_VIA_RHINE is not set -+# CONFIG_VIA_VELOCITY is not set -+CONFIG_NET_VENDOR_WIZNET=y -+# CONFIG_WIZNET_W5100 is not set -+# CONFIG_WIZNET_W5300 is not set - # CONFIG_NET_VENDOR_XILINX is not set - CONFIG_NVETHERNET=y - CONFIG_NVETHERNET_SELFTESTS=y -+# CONFIG_FDDI is not set -+# CONFIG_HIPPI is not set -+# CONFIG_NET_SB1000 is not set -+CONFIG_PHYLINK=y -+CONFIG_PHYLIB=y -+CONFIG_SWPHY=y -+# CONFIG_LED_TRIGGER_PHY is not set -+CONFIG_FIXED_PHY=y -+# CONFIG_SFP is not set -+ -+# -+# MII PHY device drivers -+# -+# CONFIG_AMD_PHY is not set -+# CONFIG_ADIN_PHY is not set - CONFIG_AQUANTIA_PHY=y -+# CONFIG_AX88796B_PHY is not set - CONFIG_BROADCOM_PHY=y -+# CONFIG_BCM54140_PHY is not set -+# CONFIG_BCM7XXX_PHY is not set -+# CONFIG_BCM84881_PHY is not set -+# CONFIG_BCM87XX_PHY is not set -+CONFIG_BCM_NET_PHYLIB=y -+# CONFIG_CICADA_PHY is not set -+# CONFIG_CORTINA_PHY is not set -+# CONFIG_DAVICOM_PHY is not set -+# CONFIG_ICPLUS_PHY is not set -+# CONFIG_LXT_PHY is not set -+# CONFIG_INTEL_XWAY_PHY is not set -+# CONFIG_LSI_ET1011C_PHY is not set - CONFIG_MARVELL_PHY=y - CONFIG_MARVELL_10G_PHY=m - CONFIG_MICREL_PHY=m -+CONFIG_MICROCHIP_PHY=m -+# CONFIG_MICROCHIP_T1_PHY is not set -+# CONFIG_MICROSEMI_PHY is not set -+# CONFIG_NATIONAL_PHY is not set -+# CONFIG_NXP_TJA11XX_PHY is not set -+# CONFIG_AT803X_PHY is not set -+# CONFIG_QSEMI_PHY is not set -+CONFIG_REALTEK_PHY=m -+# CONFIG_RENESAS_PHY is not set -+# CONFIG_ROCKCHIP_PHY is not set -+CONFIG_SMSC_PHY=m -+# CONFIG_STE10XP is not set -+# CONFIG_TERANETICS_PHY is not set -+# CONFIG_DP83822_PHY is not set -+# CONFIG_DP83TC811_PHY is not set -+# CONFIG_DP83848_PHY is not set -+# CONFIG_DP83867_PHY is not set -+# CONFIG_DP83869_PHY is not set -+# CONFIG_VITESSE_PHY is not set -+# CONFIG_XILINX_GMII2RGMII is not set -+# CONFIG_MICREL_KS8995MA is not set -+CONFIG_MDIO_DEVICE=y -+CONFIG_MDIO_BUS=y -+CONFIG_OF_MDIO=y -+CONFIG_MDIO_DEVRES=y - CONFIG_MDIO_BITBANG=y -+# CONFIG_MDIO_BCM_UNIMAC is not set -+CONFIG_MDIO_CAVIUM=m -+# CONFIG_MDIO_GPIO is not set -+# CONFIG_MDIO_HISI_FEMAC is not set -+# CONFIG_MDIO_MVUSB is not set -+# CONFIG_MDIO_MSCC_MIIM is not set -+# CONFIG_MDIO_OCTEON is not set -+# CONFIG_MDIO_IPQ4019 is not set -+# CONFIG_MDIO_IPQ8064 is not set -+CONFIG_MDIO_THUNDER=m -+ -+# -+# MDIO Multiplexers -+# -+# CONFIG_MDIO_BUS_MUX_GPIO is not set -+# CONFIG_MDIO_BUS_MUX_MULTIPLEXER is not set -+# CONFIG_MDIO_BUS_MUX_MMIOREG is not set -+ -+# -+# PCS device drivers -+# -+CONFIG_PCS_XPCS=m -+# end of PCS device drivers -+ - CONFIG_PPP=y - CONFIG_PPP_BSDCOMP=y - CONFIG_PPP_DEFLATE=y - CONFIG_PPP_FILTER=y - CONFIG_PPP_MPPE=y -+# CONFIG_PPP_MULTILINK is not set -+# CONFIG_PPPOE is not set -+# CONFIG_PPTP is not set - CONFIG_PPP_ASYNC=y - CONFIG_PPP_SYNC_TTY=y -+# CONFIG_SLIP is not set -+CONFIG_SLHC=y -+CONFIG_USB_NET_DRIVERS=y - CONFIG_USB_CATC=m - CONFIG_USB_KAWETH=m - CONFIG_USB_PEGASUS=m -@@ -516,7 +2831,12 @@ CONFIG_USB_RTL8150=m - CONFIG_USB_RTL8152=y - CONFIG_USB_LAN78XX=m - CONFIG_USB_USBNET=y -+CONFIG_USB_NET_AX8817X=y -+CONFIG_USB_NET_AX88179_178A=y -+CONFIG_USB_NET_CDCETHER=y - CONFIG_USB_NET_CDC_EEM=m -+CONFIG_USB_NET_CDC_NCM=y -+# CONFIG_USB_NET_HUAWEI_CDC_NCM is not set - CONFIG_USB_NET_CDC_MBIM=m - CONFIG_USB_NET_DM9601=m - CONFIG_USB_NET_SR9700=m -@@ -524,90 +2844,259 @@ CONFIG_USB_NET_SR9800=m - CONFIG_USB_NET_SMSC75XX=m - CONFIG_USB_NET_SMSC95XX=m - CONFIG_USB_NET_GL620A=m -+CONFIG_USB_NET_NET1080=y - CONFIG_USB_NET_PLUSB=m - CONFIG_USB_NET_MCS7830=m - CONFIG_USB_NET_RNDIS_HOST=m -+CONFIG_USB_NET_CDC_SUBSET_ENABLE=y -+CONFIG_USB_NET_CDC_SUBSET=y - CONFIG_USB_ALI_M5632=y - CONFIG_USB_AN2720=y -+CONFIG_USB_BELKIN=y -+CONFIG_USB_ARMLINUX=y -+# CONFIG_USB_EPSON2888 is not set - CONFIG_USB_KC2190=y -+CONFIG_USB_NET_ZAURUS=y - CONFIG_USB_NET_CX82310_ETH=m -+# CONFIG_USB_NET_KALMIA is not set -+# CONFIG_USB_NET_QMI_WWAN is not set -+# CONFIG_USB_HSO is not set -+# CONFIG_USB_NET_INT51X1 is not set -+# CONFIG_USB_IPHETH is not set -+# CONFIG_USB_SIERRA_NET is not set -+# CONFIG_USB_VL600 is not set -+# CONFIG_USB_NET_CH9200 is not set - CONFIG_USB_NET_AQC111=y -+# CONFIG_USB_RTL8152_SHIELD is not set -+CONFIG_WLAN=y -+# CONFIG_WIRELESS_WDS is not set -+CONFIG_WLAN_VENDOR_ADMTEK=y -+# CONFIG_ADM8211 is not set -+CONFIG_ATH_COMMON=m -+CONFIG_WLAN_VENDOR_ATH=y -+# CONFIG_ATH_DEBUG is not set -+# CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS is not set -+# CONFIG_ATH5K is not set -+# CONFIG_ATH5K_PCI is not set -+CONFIG_ATH9K_HW=m -+CONFIG_ATH9K_COMMON=m -+CONFIG_ATH9K_COMMON_DEBUG=y -+CONFIG_ATH9K_BTCOEX_SUPPORT=y - CONFIG_ATH9K=m -+CONFIG_ATH9K_PCI=y -+# CONFIG_ATH9K_AHB is not set - CONFIG_ATH9K_DEBUGFS=y - CONFIG_ATH9K_STATION_STATISTICS=y -+# CONFIG_ATH9K_TX99 is not set -+# CONFIG_ATH9K_DFS_CERTIFIED is not set -+# CONFIG_ATH9K_DYNACK is not set - CONFIG_ATH9K_WOW=y -+CONFIG_ATH9K_RFKILL=y -+# CONFIG_ATH9K_CHANNEL_CONTEXT is not set -+CONFIG_ATH9K_PCOEM=y -+# CONFIG_ATH9K_PCI_NO_EEPROM is not set - CONFIG_ATH9K_HTC=m - CONFIG_ATH9K_HTC_DEBUGFS=y -+# CONFIG_ATH9K_HWRNG is not set -+# CONFIG_ATH9K_COMMON_SPECTRAL is not set - CONFIG_CARL9170=m -+CONFIG_CARL9170_LEDS=y - CONFIG_CARL9170_DEBUGFS=y -+CONFIG_CARL9170_WPC=y -+# CONFIG_CARL9170_HWRNG is not set - CONFIG_ATH6KL=m - CONFIG_ATH6KL_SDIO=m - CONFIG_ATH6KL_USB=m -+# CONFIG_ATH6KL_DEBUG is not set -+# CONFIG_ATH6KL_TRACING is not set -+# CONFIG_ATH6KL_REGDOMAIN is not set - CONFIG_AR5523=m - CONFIG_WIL6210=m -+CONFIG_WIL6210_ISR_COR=y -+# CONFIG_WIL6210_TRACING is not set -+CONFIG_WIL6210_DEBUGFS=y - CONFIG_ATH10K=m -+CONFIG_ATH10K_CE=y - CONFIG_ATH10K_PCI=m - CONFIG_ATH10K_AHB=y - CONFIG_ATH10K_SDIO=m - CONFIG_ATH10K_USB=m -+# CONFIG_ATH10K_DEBUG is not set - CONFIG_ATH10K_DEBUGFS=y -+# CONFIG_ATH10K_SPECTRAL is not set -+# CONFIG_ATH10K_TRACING is not set -+# CONFIG_ATH10K_DFS_CERTIFIED is not set - CONFIG_WCN36XX=m - CONFIG_WCN36XX_DEBUGFS=y -+# CONFIG_ATH11K is not set -+CONFIG_WLAN_VENDOR_ATMEL=y - CONFIG_ATMEL=m -+# CONFIG_PCI_ATMEL is not set - CONFIG_AT76C50X_USB=m -+CONFIG_WLAN_VENDOR_BROADCOM=y - CONFIG_B43=m -+CONFIG_B43_BCMA=y -+CONFIG_B43_SSB=y -+CONFIG_B43_BUSES_BCMA_AND_SSB=y -+# CONFIG_B43_BUSES_BCMA is not set -+# CONFIG_B43_BUSES_SSB is not set -+CONFIG_B43_PCI_AUTOSELECT=y -+CONFIG_B43_PCICORE_AUTOSELECT=y - CONFIG_B43_SDIO=y -+CONFIG_B43_BCMA_PIO=y -+CONFIG_B43_PIO=y -+CONFIG_B43_PHY_G=y -+CONFIG_B43_PHY_N=y -+CONFIG_B43_PHY_LP=y -+CONFIG_B43_PHY_HT=y -+CONFIG_B43_LEDS=y -+CONFIG_B43_HWRNG=y -+# CONFIG_B43_DEBUG is not set - CONFIG_B43LEGACY=m -+CONFIG_B43LEGACY_PCI_AUTOSELECT=y -+CONFIG_B43LEGACY_PCICORE_AUTOSELECT=y -+CONFIG_B43LEGACY_LEDS=y -+CONFIG_B43LEGACY_HWRNG=y -+CONFIG_B43LEGACY_DEBUG=y -+CONFIG_B43LEGACY_DMA=y -+CONFIG_B43LEGACY_PIO=y -+CONFIG_B43LEGACY_DMA_AND_PIO_MODE=y -+# CONFIG_B43LEGACY_DMA_MODE is not set -+# CONFIG_B43LEGACY_PIO_MODE is not set -+CONFIG_BRCMUTIL=m -+# CONFIG_BRCMSMAC is not set - CONFIG_BRCMFMAC=m -+CONFIG_BRCMFMAC_PROTO_MSGBUF=y - # CONFIG_BRCMFMAC_SDIO is not set -+# CONFIG_BRCMFMAC_USB is not set - CONFIG_BRCMFMAC_PCIE=y -+# CONFIG_BRCM_TRACING is not set -+# CONFIG_BRCMDBG is not set -+CONFIG_WLAN_VENDOR_CISCO=y -+CONFIG_WLAN_VENDOR_INTEL=y - CONFIG_IPW2100=m -+# CONFIG_IPW2100_MONITOR is not set -+# CONFIG_IPW2100_DEBUG is not set - CONFIG_IPW2200=m -+# CONFIG_IPW2200_MONITOR is not set -+# CONFIG_IPW2200_QOS is not set -+# CONFIG_IPW2200_DEBUG is not set -+CONFIG_LIBIPW=m -+# CONFIG_LIBIPW_DEBUG is not set -+CONFIG_IWLEGACY=m -+# CONFIG_IWL4965 is not set - CONFIG_IWL3945=m -+ -+# -+# iwl3945 / iwl4965 Debugging Options -+# -+# CONFIG_IWLEGACY_DEBUG is not set -+# CONFIG_IWLEGACY_DEBUGFS is not set -+# end of iwl3945 / iwl4965 Debugging Options -+ - CONFIG_IWLWIFI=m -+CONFIG_IWLWIFI_LEDS=y - CONFIG_IWLDVM=m - CONFIG_IWLMVM=m -+CONFIG_IWLWIFI_OPMODE_MODULAR=y - CONFIG_IWLWIFI_BCAST_FILTERING=y -+ -+# -+# Debugging Options -+# - CONFIG_IWLWIFI_DEBUG=y - CONFIG_IWLWIFI_DEBUGFS=y -+CONFIG_IWLWIFI_DEVICE_TRACING=y -+# end of Debugging Options -+ -+CONFIG_WLAN_VENDOR_INTERSIL=y -+# CONFIG_HOSTAP is not set - CONFIG_HERMES=m - CONFIG_HERMES_PRISM=y -+CONFIG_HERMES_CACHE_FW_ON_INIT=y - CONFIG_PLX_HERMES=m - CONFIG_TMD_HERMES=m - CONFIG_NORTEL_HERMES=m -+# CONFIG_PCI_HERMES is not set - CONFIG_ORINOCO_USB=m - CONFIG_P54_COMMON=m - CONFIG_P54_USB=m - CONFIG_P54_PCI=m - CONFIG_P54_SPI=m -+# CONFIG_P54_SPI_DEFAULT_EEPROM is not set -+CONFIG_P54_LEDS=y -+# CONFIG_PRISM54 is not set -+CONFIG_WLAN_VENDOR_MARVELL=y - CONFIG_LIBERTAS=m -+# CONFIG_LIBERTAS_USB is not set -+# CONFIG_LIBERTAS_SDIO is not set -+# CONFIG_LIBERTAS_SPI is not set -+# CONFIG_LIBERTAS_DEBUG is not set -+# CONFIG_LIBERTAS_MESH is not set - CONFIG_LIBERTAS_THINFIRM=m -+# CONFIG_LIBERTAS_THINFIRM_DEBUG is not set -+# CONFIG_LIBERTAS_THINFIRM_USB is not set - CONFIG_MWIFIEX=m - CONFIG_MWIFIEX_SDIO=m - CONFIG_MWIFIEX_PCIE=m - CONFIG_MWIFIEX_USB=m - CONFIG_MWL8K=m -+CONFIG_WLAN_VENDOR_MEDIATEK=y - CONFIG_MT7601U=m -+CONFIG_MT76_CORE=m -+CONFIG_MT76_LEDS=y -+CONFIG_MT76_USB=m -+CONFIG_MT76x02_LIB=m -+CONFIG_MT76x02_USB=m -+CONFIG_MT76x0_COMMON=m - CONFIG_MT76x0U=m - CONFIG_MT76x0E=m -+CONFIG_MT76x2_COMMON=m - CONFIG_MT76x2E=m - CONFIG_MT76x2U=m - CONFIG_MT7603E=m -+CONFIG_MT7615_COMMON=m - CONFIG_MT7615E=m -+CONFIG_MT7663_USB_SDIO_COMMON=m - CONFIG_MT7663U=m -+# CONFIG_MT7663S is not set - CONFIG_MT7915E=m - # CONFIG_WLAN_VENDOR_MICROCHIP is not set -+CONFIG_WLAN_VENDOR_RALINK=y - CONFIG_RT2X00=m -+# CONFIG_RT2400PCI is not set -+# CONFIG_RT2500PCI is not set -+# CONFIG_RT61PCI is not set - CONFIG_RT2800PCI=m -+CONFIG_RT2800PCI_RT33XX=y -+CONFIG_RT2800PCI_RT35XX=y -+CONFIG_RT2800PCI_RT53XX=y -+CONFIG_RT2800PCI_RT3290=y - CONFIG_RT2500USB=m - CONFIG_RT73USB=m - CONFIG_RT2800USB=m -+CONFIG_RT2800USB_RT33XX=y -+CONFIG_RT2800USB_RT35XX=y - CONFIG_RT2800USB_RT3573=y - CONFIG_RT2800USB_RT53XX=y - CONFIG_RT2800USB_RT55XX=y - CONFIG_RT2800USB_UNKNOWN=y -+CONFIG_RT2800_LIB=m -+CONFIG_RT2800_LIB_MMIO=m -+CONFIG_RT2X00_LIB_MMIO=m -+CONFIG_RT2X00_LIB_PCI=m -+CONFIG_RT2X00_LIB_USB=m -+CONFIG_RT2X00_LIB=m -+CONFIG_RT2X00_LIB_FIRMWARE=y -+CONFIG_RT2X00_LIB_CRYPTO=y -+CONFIG_RT2X00_LIB_LEDS=y - CONFIG_RT2X00_LIB_DEBUGFS=y -+# CONFIG_RT2X00_DEBUG is not set -+CONFIG_WLAN_VENDOR_REALTEK=y -+# CONFIG_RTL8180 is not set - CONFIG_RTL8187=m -+CONFIG_RTL8187_LEDS=y -+CONFIG_RTL_CARDS=m - CONFIG_RTL8192CE=m - CONFIG_RTL8192SE=m - CONFIG_RTL8192DE=m -@@ -617,36 +3106,120 @@ CONFIG_RTL8188EE=m - CONFIG_RTL8192EE=m - CONFIG_RTL8821AE=m - CONFIG_RTL8192CU=m -+CONFIG_RTLWIFI=m -+CONFIG_RTLWIFI_PCI=m -+CONFIG_RTLWIFI_USB=m -+CONFIG_RTLWIFI_DEBUG=y -+CONFIG_RTL8192C_COMMON=m -+CONFIG_RTL8723_COMMON=m -+CONFIG_RTLBTCOEXIST=m - CONFIG_RTL8XXXU=m - CONFIG_RTL8XXXU_UNTESTED=y -+# CONFIG_RTW88 is not set -+CONFIG_WLAN_VENDOR_RSI=y - CONFIG_RSI_91X=m -+CONFIG_RSI_DEBUGFS=y -+CONFIG_RSI_SDIO=m -+CONFIG_RSI_USB=m - # CONFIG_RSI_COEX is not set -+CONFIG_WLAN_VENDOR_ST=y - CONFIG_CW1200=m - CONFIG_CW1200_WLAN_SDIO=m -+# CONFIG_CW1200_WLAN_SPI is not set -+CONFIG_WLAN_VENDOR_TI=y - CONFIG_WL1251=m -+# CONFIG_WL1251_SPI is not set - CONFIG_WL1251_SDIO=m - CONFIG_WL12XX=m - CONFIG_WL18XX=m -+CONFIG_WLCORE=m -+# CONFIG_WLCORE_SPI is not set - CONFIG_WLCORE_SDIO=m -+CONFIG_WILINK_PLATFORM_DATA=y -+CONFIG_WLAN_VENDOR_ZYDAS=y - CONFIG_USB_ZD1201=m - CONFIG_ZD1211RW=m -+# CONFIG_ZD1211RW_DEBUG is not set -+CONFIG_WLAN_VENDOR_QUANTENNA=y -+# CONFIG_QTNFMAC_PCIE is not set -+# CONFIG_MAC80211_HWSIM is not set -+# CONFIG_USB_NET_RNDIS_WLAN is not set -+# CONFIG_VIRT_WIFI is not set - CONFIG_BCMDHD=m -+# CONFIG_BCMDHD_SDIO is not set -+# CONFIG_BCMDHD_PCIE is not set -+# CONFIG_BCMDYNAMIC is not set -+# CONFIG_BCM43241 is not set - CONFIG_BCM4354=y -+CONFIG_BCMDHD_FW_PATH="/system/vendor/firmware/fw_bcmdhd.bin" -+CONFIG_BCMDHD_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" - CONFIG_BCMDHD_HW_OOB=y -+# CONFIG_DHD_USE_STATIC_BUF is not set - CONFIG_DHD_USE_SCHED_SCAN=y - CONFIG_BCMDHD_DISABLE_MCC=y -+# CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA is not set -+# CONFIG_BCMDHD_CUSTOM_NET_PERF_TEGRA is not set -+# CONFIG_BCMDHD_QMONITOR is not set -+# CONFIG_BCMDHD_CUSTOM_NET_BW_EST_TEGRA is not set -+# CONFIG_BCMDHD_CUSTOM_NET_DIAG_TEGRA is not set -+CONFIG_BCM4359=y -+CONFIG_BCMDHD_PCIE_FW_PATH="/system/vendor/firmware/fw_bcmdhd.bin" -+CONFIG_BCMDHD_PCIE_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" -+CONFIG_BCMDHD_CLM_PATH="/lib/firmware/brcm/bcmdhd.clm_blob" -+CONFIG_BCMDHD_PCIE_ES4_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" -+CONFIG_DHD_SET_RANDOM_MAC_VAL=0x001A11 -+# CONFIG_RTL8812AU is not set -+# CONFIG_RTL8814AU is not set -+# CONFIG_RTL8821AU is not set -+# CONFIG_RTL8821CU is not set -+# CONFIG_RTL8822BU is not set - CONFIG_RTL8822CE=m -+ -+# -+# Enable WiMAX (Networking options) to see the WiMAX drivers -+# -+# CONFIG_WAN is not set -+# CONFIG_VMXNET3 is not set -+# CONFIG_FUJITSU_ES is not set -+# CONFIG_NETDEVSIM is not set -+CONFIG_NET_FAILOVER=y - CONFIG_TEGRA_HV_NET=y -+# CONFIG_ISDN is not set -+# CONFIG_NVM is not set -+ -+# -+# Input device support -+# -+CONFIG_INPUT=y - CONFIG_INPUT_LEDS=m -+CONFIG_INPUT_FF_MEMLESS=y - CONFIG_INPUT_POLLDEV=m -+# CONFIG_INPUT_SPARSEKMAP is not set -+CONFIG_INPUT_MATRIXKMAP=m -+ -+# -+# Userland interfaces -+# - CONFIG_INPUT_MOUSEDEV=y -+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 -+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 - CONFIG_INPUT_JOYDEV=y - CONFIG_INPUT_EVDEV=y -+# CONFIG_INPUT_EVBUG is not set -+ -+# -+# Input Device Drivers -+# -+CONFIG_INPUT_KEYBOARD=y -+# CONFIG_KEYBOARD_ADC is not set - CONFIG_KEYBOARD_ADP5588=m - CONFIG_KEYBOARD_ADP5589=m - CONFIG_KEYBOARD_ATKBD=m -+# CONFIG_KEYBOARD_QT1050 is not set - CONFIG_KEYBOARD_QT1070=m - CONFIG_KEYBOARD_QT2160=m -+# CONFIG_KEYBOARD_DLINK_DIR685 is not set - CONFIG_KEYBOARD_LKKBD=m - CONFIG_KEYBOARD_GPIO=y - CONFIG_KEYBOARD_GPIO_POLLED=m -@@ -663,165 +3236,1225 @@ CONFIG_KEYBOARD_TEGRA=m - CONFIG_KEYBOARD_OPENCORES=m - CONFIG_KEYBOARD_SAMSUNG=m - CONFIG_KEYBOARD_STOWAWAY=m -+# CONFIG_KEYBOARD_SUNKBD is not set - CONFIG_KEYBOARD_OMAP4=m -+# CONFIG_KEYBOARD_TM2_TOUCHKEY is not set - CONFIG_KEYBOARD_XTKBD=m - CONFIG_KEYBOARD_CAP11XX=m - CONFIG_KEYBOARD_BCM=m -+# CONFIG_KEYBOARD_TIMED_GPIO is not set -+CONFIG_INPUT_MOUSE=y - CONFIG_MOUSE_PS2=m -+CONFIG_MOUSE_PS2_ALPS=y -+CONFIG_MOUSE_PS2_BYD=y -+CONFIG_MOUSE_PS2_LOGIPS2PP=y -+CONFIG_MOUSE_PS2_SYNAPTICS=y -+CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS=y -+CONFIG_MOUSE_PS2_CYPRESS=y -+CONFIG_MOUSE_PS2_TRACKPOINT=y -+# CONFIG_MOUSE_PS2_ELANTECH is not set -+# CONFIG_MOUSE_PS2_SENTELIC is not set -+# CONFIG_MOUSE_PS2_TOUCHKIT is not set -+CONFIG_MOUSE_PS2_FOCALTECH=y -+CONFIG_MOUSE_PS2_SMBUS=y - CONFIG_MOUSE_SERIAL=m - CONFIG_MOUSE_APPLETOUCH=m - CONFIG_MOUSE_BCM5974=m - CONFIG_MOUSE_CYAPA=m -+# CONFIG_MOUSE_ELAN_I2C is not set - CONFIG_MOUSE_VSXXXAA=m - CONFIG_MOUSE_GPIO=m - CONFIG_MOUSE_SYNAPTICS_I2C=m - CONFIG_MOUSE_SYNAPTICS_USB=m - CONFIG_INPUT_JOYSTICK=y -+# CONFIG_JOYSTICK_ANALOG is not set -+# CONFIG_JOYSTICK_A3D is not set -+# CONFIG_JOYSTICK_ADC is not set -+# CONFIG_JOYSTICK_ADI is not set -+# CONFIG_JOYSTICK_COBRA is not set -+# CONFIG_JOYSTICK_GF2K is not set -+# CONFIG_JOYSTICK_GRIP is not set -+# CONFIG_JOYSTICK_GRIP_MP is not set -+# CONFIG_JOYSTICK_GUILLEMOT is not set -+# CONFIG_JOYSTICK_INTERACT is not set -+# CONFIG_JOYSTICK_SIDEWINDER is not set -+# CONFIG_JOYSTICK_TMDC is not set -+# CONFIG_JOYSTICK_IFORCE is not set -+# CONFIG_JOYSTICK_WARRIOR is not set -+# CONFIG_JOYSTICK_MAGELLAN is not set -+# CONFIG_JOYSTICK_SPACEORB is not set -+# CONFIG_JOYSTICK_SPACEBALL is not set -+# CONFIG_JOYSTICK_STINGER is not set -+# CONFIG_JOYSTICK_TWIDJOY is not set -+# CONFIG_JOYSTICK_ZHENHUA is not set -+# CONFIG_JOYSTICK_AS5011 is not set -+# CONFIG_JOYSTICK_JOYDUMP is not set - CONFIG_JOYSTICK_XPAD=y -+# CONFIG_JOYSTICK_XPAD_FF is not set -+# CONFIG_JOYSTICK_XPAD_LEDS is not set -+# CONFIG_JOYSTICK_PSXPAD_SPI is not set -+# CONFIG_JOYSTICK_PXRC is not set -+# CONFIG_JOYSTICK_FSIA6B is not set - CONFIG_INPUT_TABLET=y -+# CONFIG_TABLET_USB_ACECAD is not set -+# CONFIG_TABLET_USB_AIPTEK is not set -+# CONFIG_TABLET_USB_GTCO is not set -+# CONFIG_TABLET_USB_HANWANG is not set -+# CONFIG_TABLET_USB_KBTAB is not set -+# CONFIG_TABLET_USB_PEGASUS is not set -+# CONFIG_TABLET_SERIAL_WACOM4 is not set - CONFIG_INPUT_TOUCHSCREEN=y -+CONFIG_TOUCHSCREEN_PROPERTIES=y -+# CONFIG_TOUCHSCREEN_ADS7846 is not set -+# CONFIG_TOUCHSCREEN_AD7877 is not set -+# CONFIG_TOUCHSCREEN_AD7879 is not set -+# CONFIG_TOUCHSCREEN_ADC is not set -+# CONFIG_TOUCHSCREEN_AR1021_I2C is not set - CONFIG_TOUCHSCREEN_ATMEL_MXT=m -+# CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 is not set -+# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set -+# CONFIG_TOUCHSCREEN_BU21013 is not set -+# CONFIG_TOUCHSCREEN_BU21029 is not set -+# CONFIG_TOUCHSCREEN_CHIPONE_ICN8318 is not set -+# CONFIG_TOUCHSCREEN_CHIPONE_ICN8505 is not set -+# CONFIG_TOUCHSCREEN_CY8CTMA140 is not set -+# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set -+# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set -+# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set -+# CONFIG_TOUCHSCREEN_DYNAPRO is not set -+# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set -+# CONFIG_TOUCHSCREEN_EETI is not set -+# CONFIG_TOUCHSCREEN_EGALAX is not set -+# CONFIG_TOUCHSCREEN_EGALAX_SERIAL is not set -+# CONFIG_TOUCHSCREEN_EXC3000 is not set -+# CONFIG_TOUCHSCREEN_FUJITSU is not set -+# CONFIG_TOUCHSCREEN_GOODIX is not set -+# CONFIG_TOUCHSCREEN_HIDEEP is not set -+# CONFIG_TOUCHSCREEN_ILI210X is not set -+# CONFIG_TOUCHSCREEN_S6SY761 is not set -+# CONFIG_TOUCHSCREEN_GUNZE is not set -+# CONFIG_TOUCHSCREEN_EKTF2127 is not set -+# CONFIG_TOUCHSCREEN_ELAN is not set -+# CONFIG_TOUCHSCREEN_ELO is not set -+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set -+# CONFIG_TOUCHSCREEN_WACOM_I2C is not set -+# CONFIG_TOUCHSCREEN_MAX11801 is not set -+# CONFIG_TOUCHSCREEN_MCS5000 is not set -+# CONFIG_TOUCHSCREEN_MMS114 is not set -+# CONFIG_TOUCHSCREEN_MELFAS_MIP4 is not set -+# CONFIG_TOUCHSCREEN_MTOUCH is not set -+# CONFIG_TOUCHSCREEN_IMX6UL_TSC is not set -+# CONFIG_TOUCHSCREEN_INEXIO is not set -+# CONFIG_TOUCHSCREEN_MK712 is not set -+# CONFIG_TOUCHSCREEN_PENMOUNT is not set -+# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set -+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set -+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set -+# CONFIG_TOUCHSCREEN_PIXCIR is not set -+# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set -+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set -+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set -+# CONFIG_TOUCHSCREEN_TSC_SERIO is not set -+# CONFIG_TOUCHSCREEN_TSC2004 is not set -+# CONFIG_TOUCHSCREEN_TSC2005 is not set -+# CONFIG_TOUCHSCREEN_TSC2007 is not set -+# CONFIG_TOUCHSCREEN_RM_TS is not set -+# CONFIG_TOUCHSCREEN_SILEAD is not set -+# CONFIG_TOUCHSCREEN_SIS_I2C is not set -+# CONFIG_TOUCHSCREEN_ST1232 is not set -+# CONFIG_TOUCHSCREEN_STMFTS is not set -+# CONFIG_TOUCHSCREEN_SUR40 is not set -+# CONFIG_TOUCHSCREEN_SURFACE3_SPI is not set -+# CONFIG_TOUCHSCREEN_SX8654 is not set -+# CONFIG_TOUCHSCREEN_TPS6507X is not set -+# CONFIG_TOUCHSCREEN_ZET6223 is not set -+# CONFIG_TOUCHSCREEN_ZFORCE is not set -+# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set -+# CONFIG_TOUCHSCREEN_IQS5XX is not set -+# CONFIG_TOUCHSCREEN_ZINITIX is not set -+# CONFIG_TOUCHSCREEN_NVIDIA_ATMEL_MXT is not set -+# CONFIG_TOUCHSCREEN_LR388K7 is not set -+# CONFIG_TOUCHSCREEN_RM31080A is not set -+# CONFIG_TOUCHSCREEN_EXC80 is not set -+# CONFIG_TOUCHSCREEN_EXC80_USB is not set - CONFIG_INPUT_MISC=y -+# CONFIG_INPUT_AD714X is not set -+# CONFIG_INPUT_ATMEL_CAPTOUCH is not set -+# CONFIG_INPUT_BMA150 is not set -+# CONFIG_INPUT_E3X0_BUTTON is not set -+# CONFIG_INPUT_MMA8450 is not set -+# CONFIG_INPUT_GPIO_BEEPER is not set -+# CONFIG_INPUT_GPIO_DECODER is not set -+# CONFIG_INPUT_GPIO_VIBRA is not set -+# CONFIG_INPUT_ATI_REMOTE2 is not set -+# CONFIG_INPUT_KEYSPAN_REMOTE is not set -+# CONFIG_INPUT_KXTJ9 is not set -+# CONFIG_INPUT_POWERMATE is not set -+# CONFIG_INPUT_YEALINK is not set -+# CONFIG_INPUT_CM109 is not set -+# CONFIG_INPUT_REGULATOR_HAPTIC is not set - CONFIG_INPUT_UINPUT=y -+# CONFIG_INPUT_PCF8574 is not set -+# CONFIG_INPUT_PWM_BEEPER is not set -+# CONFIG_INPUT_PWM_VIBRA is not set -+# CONFIG_INPUT_RK805_PWRKEY is not set -+# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set -+# CONFIG_INPUT_ADXL34X is not set -+# CONFIG_INPUT_IMS_PCU is not set -+# CONFIG_INPUT_IQS269A is not set -+# CONFIG_INPUT_CMA3000 is not set -+# CONFIG_INPUT_SOC_BUTTON_ARRAY is not set -+# CONFIG_INPUT_DRV260X_HAPTICS is not set -+# CONFIG_INPUT_DRV2665_HAPTICS is not set -+# CONFIG_INPUT_DRV2667_HAPTICS is not set -+CONFIG_RMI4_CORE=m -+# CONFIG_RMI4_I2C is not set -+# CONFIG_RMI4_SPI is not set -+# CONFIG_RMI4_SMB is not set -+CONFIG_RMI4_F03=y -+CONFIG_RMI4_F03_SERIO=m -+CONFIG_RMI4_2D_SENSOR=y -+CONFIG_RMI4_F11=y -+CONFIG_RMI4_F12=y -+CONFIG_RMI4_F30=y -+# CONFIG_RMI4_F34 is not set -+# CONFIG_RMI4_F3A is not set -+# CONFIG_RMI4_F54 is not set -+# CONFIG_RMI4_F55 is not set -+ -+# -+# Hardware I/O ports -+# -+CONFIG_SERIO=y - CONFIG_SERIO_SERPORT=m - CONFIG_SERIO_AMBAKMI=m -+# CONFIG_SERIO_PCIPS2 is not set -+CONFIG_SERIO_LIBPS2=m -+# CONFIG_SERIO_RAW is not set -+# CONFIG_SERIO_ALTERA_PS2 is not set -+# CONFIG_SERIO_PS2MULT is not set -+# CONFIG_SERIO_ARC_PS2 is not set -+# CONFIG_SERIO_APBPS2 is not set -+# CONFIG_SERIO_GPIO_PS2 is not set -+# CONFIG_USERIO is not set -+# CONFIG_GAMEPORT is not set -+# end of Hardware I/O ports -+# end of Input device support -+ -+# CONFIG_INPUT_CFBOOST is not set -+ -+# -+# Character devices -+# -+CONFIG_TTY=y -+CONFIG_VT=y -+CONFIG_CONSOLE_TRANSLATIONS=y -+CONFIG_VT_CONSOLE=y -+CONFIG_VT_CONSOLE_SLEEP=y -+CONFIG_HW_CONSOLE=y -+CONFIG_VT_HW_CONSOLE_BINDING=y -+CONFIG_UNIX98_PTYS=y -+CONFIG_LEGACY_PTYS=y - CONFIG_LEGACY_PTY_COUNT=16 -+CONFIG_LDISC_AUTOLOAD=y -+ -+# -+# Serial drivers -+# -+CONFIG_SERIAL_EARLYCON=y - CONFIG_SERIAL_8250=y -+CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y -+CONFIG_SERIAL_8250_PNP=y -+CONFIG_SERIAL_8250_16550A_VARIANTS=y -+# CONFIG_SERIAL_8250_FINTEK is not set - CONFIG_SERIAL_8250_CONSOLE=y -+CONFIG_SERIAL_8250_DMA=y -+CONFIG_SERIAL_8250_PCI=y -+CONFIG_SERIAL_8250_EXAR=y -+CONFIG_SERIAL_8250_NR_UARTS=4 -+CONFIG_SERIAL_8250_RUNTIME_UARTS=4 - CONFIG_SERIAL_8250_EXTENDED=y -+# CONFIG_SERIAL_8250_MANY_PORTS is not set -+# CONFIG_SERIAL_8250_ASPEED_VUART is not set - CONFIG_SERIAL_8250_SHARE_IRQ=y -+# CONFIG_SERIAL_8250_DETECT_IRQ is not set -+# CONFIG_SERIAL_8250_RSA is not set -+CONFIG_SERIAL_8250_DWLIB=y -+CONFIG_SERIAL_8250_FSL=y - CONFIG_SERIAL_8250_DW=m -+# CONFIG_SERIAL_8250_RT288X is not set -+CONFIG_SERIAL_8250_TEGRA=y - CONFIG_SERIAL_OF_PLATFORM=y -+ -+# -+# Non-8250 serial port support -+# -+# CONFIG_SERIAL_AMBA_PL010 is not set - CONFIG_SERIAL_AMBA_PL011=y - CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set - CONFIG_SERIAL_TEGRA=y - CONFIG_SERIAL_TEGRA_TCU=y -+CONFIG_SERIAL_TEGRA_TCU_CONSOLE=y -+# CONFIG_SERIAL_MAX3100 is not set -+# CONFIG_SERIAL_MAX310X is not set -+# CONFIG_SERIAL_UARTLITE is not set -+CONFIG_SERIAL_CORE=y -+CONFIG_SERIAL_CORE_CONSOLE=y -+# CONFIG_SERIAL_JSM is not set -+# CONFIG_SERIAL_SIFIVE is not set -+# CONFIG_SERIAL_SCCNXP is not set -+# CONFIG_SERIAL_SC16IS7XX is not set -+# CONFIG_SERIAL_ALTERA_JTAGUART is not set -+# CONFIG_SERIAL_ALTERA_UART is not set -+# CONFIG_SERIAL_IFX6X60 is not set - CONFIG_SERIAL_XILINX_PS_UART=y -+# CONFIG_SERIAL_XILINX_PS_UART_CONSOLE is not set -+# CONFIG_SERIAL_ARC is not set -+# CONFIG_SERIAL_RP2 is not set - CONFIG_SERIAL_FSL_LPUART=y -+# CONFIG_SERIAL_FSL_LPUART_CONSOLE is not set -+# CONFIG_SERIAL_FSL_LINFLEXUART is not set -+# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set -+# CONFIG_SERIAL_SPRD is not set -+# end of Serial drivers -+ -+CONFIG_SERIAL_MCTRL_GPIO=y - CONFIG_TEGRA_COMBINED_UART_EARLY=y -+# CONFIG_TEGRA_HV_COMM is not set -+# CONFIG_SERIAL_TEGRA_NOHW is not set -+# CONFIG_SERIAL_NONSTANDARD is not set -+# CONFIG_N_GSM is not set -+# CONFIG_NOZOMI is not set -+# CONFIG_NULL_TTY is not set -+# CONFIG_TRACE_SINK is not set -+CONFIG_HVC_DRIVER=y -+# CONFIG_HVC_DCC is not set - CONFIG_SERIAL_DEV_BUS=y -+CONFIG_SERIAL_DEV_CTRL_TTYPORT=y -+# CONFIG_TTY_PRINTK is not set - CONFIG_VIRTIO_CONSOLE=y -+# CONFIG_IPMI_HANDLER is not set -+# CONFIG_IPMB_DEVICE_INTERFACE is not set -+CONFIG_HW_RANDOM=m -+# CONFIG_HW_RANDOM_TIMERIOMEM is not set -+# CONFIG_HW_RANDOM_BA431 is not set -+# CONFIG_HW_RANDOM_VIRTIO is not set -+CONFIG_HW_RANDOM_HISI_V2=m -+CONFIG_HW_RANDOM_CAVIUM=m -+CONFIG_HW_RANDOM_OPTEE=m -+# CONFIG_HW_RANDOM_CCTRNG is not set -+# CONFIG_HW_RANDOM_XIPHERA is not set -+# CONFIG_APPLICOM is not set -+CONFIG_DEVMEM=y -+# CONFIG_RAW_DRIVER is not set -+CONFIG_DEVPORT=y -+# CONFIG_TCG_TPM is not set -+# CONFIG_XILLYBUS is not set -+# end of Character devices -+ -+# CONFIG_RANDOM_TRUST_CPU is not set -+# CONFIG_RANDOM_TRUST_BOOTLOADER is not set -+ -+# -+# I2C support -+# -+CONFIG_I2C=y -+CONFIG_ACPI_I2C_OPREGION=y -+CONFIG_I2C_BOARDINFO=y -+CONFIG_I2C_COMPAT=y - CONFIG_I2C_CHARDEV=y - CONFIG_I2C_MUX=y -+ -+# -+# Multiplexer I2C Chip support -+# -+# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set - CONFIG_I2C_MUX_GPIO=y -+# CONFIG_I2C_MUX_GPMUX is not set -+# CONFIG_I2C_MUX_LTC4306 is not set -+# CONFIG_I2C_MUX_PCA9541 is not set - CONFIG_I2C_MUX_PCA954x=y -+# CONFIG_I2C_MUX_PINCTRL is not set -+# CONFIG_I2C_MUX_REG is not set -+# CONFIG_I2C_DEMUX_PINCTRL is not set -+# CONFIG_I2C_MUX_MLXCPLD is not set -+# end of Multiplexer I2C Chip support -+ -+CONFIG_I2C_HELPER_AUTO=y -+CONFIG_I2C_SMBUS=m -+CONFIG_I2C_ALGOBIT=y -+CONFIG_I2C_ALGOPCA=m -+ -+# -+# I2C Hardware Bus support -+# -+ -+# -+# PC SMBus host controller drivers -+# - CONFIG_I2C_ALI1535=m - CONFIG_I2C_ALI1563=m - CONFIG_I2C_ALI15X3=m - CONFIG_I2C_AMD756=m - CONFIG_I2C_AMD8111=m -+# CONFIG_I2C_AMD_MP2 is not set - CONFIG_I2C_I801=m - CONFIG_I2C_ISCH=m - CONFIG_I2C_PIIX4=m - CONFIG_I2C_NFORCE2=m -+# CONFIG_I2C_NVIDIA_GPU is not set -+# CONFIG_I2C_SIS5595 is not set - CONFIG_I2C_SIS630=m -+# CONFIG_I2C_SIS96X is not set -+# CONFIG_I2C_VIA is not set - CONFIG_I2C_VIAPRO=m -+ -+# -+# ACPI drivers -+# -+# CONFIG_I2C_SCMI is not set -+ -+# -+# I2C system bus drivers (mostly embedded / system-on-chip) -+# - CONFIG_I2C_CADENCE=m - CONFIG_I2C_CBUS_GPIO=m -+CONFIG_I2C_DESIGNWARE_CORE=m -+# CONFIG_I2C_DESIGNWARE_SLAVE is not set - CONFIG_I2C_DESIGNWARE_PLATFORM=m - CONFIG_I2C_DESIGNWARE_PCI=m - CONFIG_I2C_EMEV2=m - CONFIG_I2C_GPIO=m -+# CONFIG_I2C_GPIO_FAULT_INJECTOR is not set - CONFIG_I2C_NOMADIK=m - CONFIG_I2C_OCORES=m - CONFIG_I2C_PCA_PLATFORM=m - CONFIG_I2C_RK3X=m - CONFIG_I2C_SIMTEC=m - CONFIG_I2C_TEGRA=y -+CONFIG_I2C_TEGRA_BPMP=y -+# CONFIG_I2C_THUNDERX is not set -+# CONFIG_I2C_XILINX is not set -+ -+# -+# External I2C/SMBus adapter drivers -+# - CONFIG_I2C_DIOLAN_U2C=m -+# CONFIG_I2C_ROBOTFUZZ_OSIF is not set -+# CONFIG_I2C_TAOS_EVM is not set -+# CONFIG_I2C_TINY_USB is not set -+ -+# -+# Other I2C/SMBus bus drivers -+# -+# CONFIG_I2C_TEGRA_VI is not set - CONFIG_I2C_NVVRS11=m - CONFIG_I2C_TEGRA_CAMRTC=y -+CONFIG_I2C_TEGRA_HV=y - CONFIG_I2C_TEGRA_SLAVE=m - CONFIG_I2C_TEGRA194_SLAVE=m -+# end of I2C Hardware Bus support -+ - CONFIG_I2C_STUB=m -+CONFIG_I2C_SLAVE=y - CONFIG_I2C_SLAVE_EEPROM=m -+# CONFIG_I2C_SLAVE_TESTUNIT is not set -+# CONFIG_I2C_DEBUG_CORE is not set -+# CONFIG_I2C_DEBUG_ALGO is not set -+# CONFIG_I2C_DEBUG_BUS is not set -+# end of I2C support -+ -+# CONFIG_I3C is not set - CONFIG_SPI=y -+# CONFIG_SPI_DEBUG is not set -+CONFIG_SPI_MASTER=y -+CONFIG_SPI_MEM=y -+ -+# -+# SPI Master Controller Drivers -+# -+# CONFIG_SPI_ALTERA is not set -+# CONFIG_SPI_AXI_SPI_ENGINE is not set -+# CONFIG_SPI_BITBANG is not set - CONFIG_SPI_CADENCE=m - CONFIG_SPI_CADENCE_QUADSPI=y -+# CONFIG_SPI_DESIGNWARE is not set -+# CONFIG_SPI_HISI_SFC_V3XX is not set - CONFIG_SPI_NXP_FLEXSPI=y -+# CONFIG_SPI_GPIO is not set -+# CONFIG_SPI_FSL_SPI is not set -+# CONFIG_SPI_OC_TINY is not set -+# CONFIG_SPI_PL022 is not set - CONFIG_SPI_PXA2XX=m -+CONFIG_SPI_PXA2XX_PCI=m -+# CONFIG_SPI_ROCKCHIP is not set - CONFIG_SPI_SC18IS602=m -+# CONFIG_SPI_SIFIVE is not set -+# CONFIG_SPI_MXIC is not set - CONFIG_SPI_TEGRA114=m - CONFIG_SPI_TEGRA124_SLAVE=y - CONFIG_SPI_TEGRA194_SLAVE=y -+# CONFIG_SPI_TEGRA20_SFLASH is not set -+# CONFIG_SPI_TEGRA20_SLINK is not set - CONFIG_QSPI_TEGRA23x=y - CONFIG_QSPI_TEGRA210=m -+CONFIG_QSPI_TEGRA=y -+# CONFIG_SPI_THUNDERX is not set - CONFIG_SPI_XCOMM=m -+# CONFIG_SPI_XILINX is not set - CONFIG_SPI_ZYNQMP_GQSPI=m -+# CONFIG_SPI_AMD is not set -+ -+# -+# SPI Multiplexer support -+# -+# CONFIG_SPI_MUX is not set -+ -+# -+# SPI Protocol Masters -+# - CONFIG_SPI_SPIDEV=m -+# CONFIG_SPI_LOOPBACK_TEST is not set - CONFIG_SPI_TLE62X0=m -+# CONFIG_SPI_SLAVE is not set -+CONFIG_SPI_DYNAMIC=y -+# CONFIG_SPI_AURIX_TEGRA is not set - CONFIG_SPMI=m -+# CONFIG_HSI is not set -+CONFIG_PPS=y - CONFIG_PPS_DEBUG=y -+ -+# -+# PPS clients support -+# -+# CONFIG_PPS_CLIENT_KTIMER is not set -+# CONFIG_PPS_CLIENT_LDISC is not set - CONFIG_PPS_CLIENT_GPIO=y -+ -+# -+# PPS generators support -+# -+ -+# -+# PTP clock support -+# -+CONFIG_PTP_1588_CLOCK=y -+ -+# -+# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. -+# -+# CONFIG_PTP_1588_CLOCK_IDT82P33 is not set -+# CONFIG_PTP_1588_CLOCK_IDTCM is not set -+# end of PTP clock support -+ -+CONFIG_PINCTRL=y -+CONFIG_PINCTRL_TEGRA186=y - CONFIG_PINCTRL_TEGRA186_DPAUX=y -+CONFIG_PINCTRL_TEGRA194=y - CONFIG_PINCTRL_TEGRA234_DPAUX=y -+CONFIG_PINCTRL_TEGRA234=y -+CONFIG_GENERIC_PINCTRL_GROUPS=y -+CONFIG_PINMUX=y -+CONFIG_GENERIC_PINMUX_FUNCTIONS=y -+CONFIG_PINCONF=y -+CONFIG_GENERIC_PINCONF=y -+# CONFIG_DEBUG_PINCTRL is not set -+# CONFIG_PINCTRL_AMD is not set -+# CONFIG_PINCTRL_MCP23S08 is not set - CONFIG_PINCTRL_SINGLE=y -+# CONFIG_PINCTRL_SX150X is not set -+# CONFIG_PINCTRL_STMFX is not set - CONFIG_PINCTRL_MAX77620=y -+# CONFIG_PINCTRL_RK805 is not set -+# CONFIG_PINCTRL_OCELOT is not set -+ -+# -+# Renesas pinctrl drivers -+# -+# end of Renesas pinctrl drivers -+ -+CONFIG_PINCTRL_TEGRA=y -+CONFIG_PINCTRL_TEGRA210=y -+CONFIG_PINCTRL_TEGRA_XUSB=y -+CONFIG_GPIOLIB=y -+CONFIG_GPIOLIB_FASTPATH_LIMIT=512 -+CONFIG_OF_GPIO=y -+CONFIG_GPIO_ACPI=y -+CONFIG_GPIOLIB_IRQCHIP=y -+CONFIG_DEBUG_GPIO=y - CONFIG_GPIO_SYSFS=y -+CONFIG_GPIO_CDEV=y -+CONFIG_GPIO_CDEV_V1=y -+CONFIG_GPIO_GENERIC=y -+ -+# -+# Memory mapped GPIO drivers -+# -+# CONFIG_GPIO_74XX_MMIO is not set -+# CONFIG_GPIO_ALTERA is not set -+# CONFIG_GPIO_AMDPT is not set -+# CONFIG_GPIO_CADENCE is not set -+# CONFIG_GPIO_DWAPB is not set -+# CONFIG_GPIO_EXAR is not set -+# CONFIG_GPIO_FTGPIO010 is not set -+CONFIG_GPIO_GENERIC_PLATFORM=y -+# CONFIG_GPIO_GRGPIO is not set -+# CONFIG_GPIO_HLWD is not set -+# CONFIG_GPIO_LOGICVC is not set - CONFIG_GPIO_MB86S7X=y -+# CONFIG_GPIO_PL061 is not set -+# CONFIG_GPIO_SAMA5D2_PIOBU is not set -+# CONFIG_GPIO_SIFIVE is not set -+# CONFIG_GPIO_SYSCON is not set -+CONFIG_GPIO_TEGRA=y -+CONFIG_GPIO_TEGRA186=y -+# CONFIG_GPIO_XGENE is not set -+# CONFIG_GPIO_XILINX is not set -+# CONFIG_GPIO_AMD_FCH is not set -+# end of Memory mapped GPIO drivers -+ -+# -+# I2C GPIO expanders -+# -+# CONFIG_GPIO_ADP5588 is not set -+# CONFIG_GPIO_ADNP is not set -+# CONFIG_GPIO_GW_PLD is not set -+# CONFIG_GPIO_MAX7300 is not set -+# CONFIG_GPIO_MAX732X is not set - CONFIG_GPIO_PCA953X=y - CONFIG_GPIO_PCA953X_IRQ=y -+# CONFIG_GPIO_PCA9570 is not set -+# CONFIG_GPIO_PCF857X is not set -+# CONFIG_GPIO_TPIC2810 is not set -+# end of I2C GPIO expanders -+ -+# -+# MFD GPIO expanders -+# - CONFIG_GPIO_BD9571MWV=m - CONFIG_GPIO_MAX77620=y -+# end of MFD GPIO expanders -+ -+# -+# PCI GPIO expanders -+# -+# CONFIG_GPIO_BT8XX is not set -+# CONFIG_GPIO_PCI_IDIO_16 is not set -+# CONFIG_GPIO_PCIE_IDIO_24 is not set -+# CONFIG_GPIO_RDC321X is not set -+# end of PCI GPIO expanders -+ -+# -+# SPI GPIO expanders -+# -+# CONFIG_GPIO_74X164 is not set -+# CONFIG_GPIO_MAX3191X is not set -+# CONFIG_GPIO_MAX7301 is not set -+# CONFIG_GPIO_MC33880 is not set -+# CONFIG_GPIO_PISOSR is not set -+# CONFIG_GPIO_XRA1403 is not set -+# end of SPI GPIO expanders -+ -+# -+# USB GPIO expanders -+# -+# end of USB GPIO expanders -+ -+# CONFIG_GPIO_AGGREGATOR is not set -+CONFIG_GPIO_MOCKUP=y - CONFIG_GPIO_TMPM32X_I2C=y -+# CONFIG_W1 is not set -+CONFIG_POWER_RESET=y -+# CONFIG_POWER_RESET_BRCMSTB is not set -+# CONFIG_POWER_RESET_GPIO is not set -+# CONFIG_POWER_RESET_GPIO_RESTART is not set - CONFIG_POWER_RESET_MAX77620=y -+# CONFIG_POWER_RESET_LTC2952 is not set -+# CONFIG_POWER_RESET_RESTART is not set -+# CONFIG_POWER_RESET_VEXPRESS is not set -+# CONFIG_POWER_RESET_XGENE is not set -+# CONFIG_POWER_RESET_SYSCON is not set -+# CONFIG_POWER_RESET_SYSCON_POWEROFF is not set -+# CONFIG_SYSCON_REBOOT_MODE is not set -+# CONFIG_NVMEM_REBOOT_MODE is not set -+CONFIG_SYSTEM_PMIC=y -+# CONFIG_POWER_OFF_TMPM32X_I2C is not set -+CONFIG_POWER_SUPPLY=y -+# CONFIG_POWER_SUPPLY_DEBUG is not set -+CONFIG_POWER_SUPPLY_HWMON=y -+# CONFIG_PDA_POWER is not set -+# CONFIG_GENERIC_ADC_BATTERY is not set -+# CONFIG_TEST_POWER is not set -+# CONFIG_CHARGER_ADP5061 is not set -+# CONFIG_BATTERY_CW2015 is not set -+# CONFIG_BATTERY_DS2780 is not set -+# CONFIG_BATTERY_DS2781 is not set -+# CONFIG_BATTERY_DS2782 is not set - CONFIG_BATTERY_SBS=m -+# CONFIG_CHARGER_SBS is not set -+# CONFIG_MANAGER_SBS is not set - CONFIG_BATTERY_BQ27XXX=y -+CONFIG_BATTERY_BQ27XXX_I2C=y -+# CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM is not set -+# CONFIG_BATTERY_MAX17040 is not set -+# CONFIG_BATTERY_MAX17042 is not set -+# CONFIG_CHARGER_ISP1704 is not set -+# CONFIG_CHARGER_MAX8903 is not set -+# CONFIG_CHARGER_LP8727 is not set -+# CONFIG_CHARGER_GPIO is not set -+# CONFIG_CHARGER_MANAGER is not set -+# CONFIG_CHARGER_LT3651 is not set -+# CONFIG_CHARGER_DETECTOR_MAX14656 is not set -+# CONFIG_CHARGER_BQ2415X is not set -+# CONFIG_CHARGER_BQ24190 is not set -+# CONFIG_CHARGER_BQ24257 is not set -+# CONFIG_CHARGER_BQ24735 is not set -+# CONFIG_CHARGER_BQ2515X is not set -+# CONFIG_CHARGER_BQ25890 is not set -+# CONFIG_CHARGER_BQ25980 is not set -+# CONFIG_CHARGER_SMB347 is not set -+# CONFIG_BATTERY_GAUGE_LTC2941 is not set -+# CONFIG_BATTERY_RT5033 is not set -+# CONFIG_CHARGER_RT9455 is not set -+# CONFIG_CHARGER_UCS1002 is not set -+# CONFIG_CHARGER_BD99954 is not set -+CONFIG_HWMON=y -+# CONFIG_HWMON_DEBUG_CHIP is not set -+ -+# -+# Native drivers -+# -+# CONFIG_SENSORS_AD7314 is not set -+# CONFIG_SENSORS_AD7414 is not set -+# CONFIG_SENSORS_AD7418 is not set -+# CONFIG_SENSORS_ADM1021 is not set -+# CONFIG_SENSORS_ADM1025 is not set -+# CONFIG_SENSORS_ADM1026 is not set -+# CONFIG_SENSORS_ADM1029 is not set -+# CONFIG_SENSORS_ADM1031 is not set -+# CONFIG_SENSORS_ADM1177 is not set -+# CONFIG_SENSORS_ADM9240 is not set -+# CONFIG_SENSORS_ADT7310 is not set -+# CONFIG_SENSORS_ADT7410 is not set -+# CONFIG_SENSORS_ADT7411 is not set -+# CONFIG_SENSORS_ADT7462 is not set -+# CONFIG_SENSORS_ADT7470 is not set -+# CONFIG_SENSORS_ADT7475 is not set -+# CONFIG_SENSORS_AS370 is not set -+# CONFIG_SENSORS_ASC7621 is not set -+# CONFIG_SENSORS_AXI_FAN_CONTROL is not set -+# CONFIG_SENSORS_ARM_SCPI is not set -+# CONFIG_SENSORS_ASPEED is not set -+# CONFIG_SENSORS_ATXP1 is not set -+# CONFIG_SENSORS_CORSAIR_CPRO is not set -+# CONFIG_SENSORS_DRIVETEMP is not set -+# CONFIG_SENSORS_DS620 is not set -+# CONFIG_SENSORS_DS1621 is not set -+# CONFIG_SENSORS_I5K_AMB is not set -+# CONFIG_SENSORS_F71805F is not set -+# CONFIG_SENSORS_F71882FG is not set -+# CONFIG_SENSORS_F75375S is not set -+# CONFIG_SENSORS_FTSTEUTATES is not set -+# CONFIG_SENSORS_GL518SM is not set -+# CONFIG_SENSORS_GL520SM is not set -+# CONFIG_SENSORS_G760A is not set -+# CONFIG_SENSORS_G762 is not set -+# CONFIG_SENSORS_GPIO_FAN is not set -+# CONFIG_SENSORS_HIH6130 is not set -+# CONFIG_SENSORS_IIO_HWMON is not set -+# CONFIG_SENSORS_IT87 is not set -+# CONFIG_SENSORS_JC42 is not set -+# CONFIG_SENSORS_POWR1220 is not set -+# CONFIG_SENSORS_LINEAGE is not set -+# CONFIG_SENSORS_LTC2945 is not set -+# CONFIG_SENSORS_LTC2947_I2C is not set -+# CONFIG_SENSORS_LTC2947_SPI is not set -+# CONFIG_SENSORS_LTC2990 is not set -+# CONFIG_SENSORS_LTC4151 is not set -+# CONFIG_SENSORS_LTC4215 is not set -+# CONFIG_SENSORS_LTC4222 is not set -+# CONFIG_SENSORS_LTC4245 is not set -+# CONFIG_SENSORS_LTC4260 is not set -+# CONFIG_SENSORS_LTC4261 is not set -+# CONFIG_SENSORS_MAX1111 is not set -+# CONFIG_SENSORS_MAX16065 is not set -+# CONFIG_SENSORS_MAX1619 is not set -+# CONFIG_SENSORS_MAX1668 is not set -+# CONFIG_SENSORS_MAX197 is not set -+# CONFIG_SENSORS_MAX31722 is not set -+# CONFIG_SENSORS_MAX31730 is not set -+# CONFIG_SENSORS_MAX6621 is not set -+# CONFIG_SENSORS_MAX6639 is not set -+# CONFIG_SENSORS_MAX6642 is not set -+# CONFIG_SENSORS_MAX6650 is not set -+# CONFIG_SENSORS_MAX6697 is not set -+# CONFIG_SENSORS_MAX31790 is not set -+# CONFIG_SENSORS_MCP3021 is not set -+# CONFIG_SENSORS_TC654 is not set -+# CONFIG_SENSORS_MR75203 is not set -+# CONFIG_SENSORS_ADCXX is not set -+# CONFIG_SENSORS_LM63 is not set -+# CONFIG_SENSORS_LM70 is not set -+# CONFIG_SENSORS_LM73 is not set -+# CONFIG_SENSORS_LM75 is not set -+# CONFIG_SENSORS_LM77 is not set -+# CONFIG_SENSORS_LM78 is not set -+# CONFIG_SENSORS_LM80 is not set -+# CONFIG_SENSORS_LM83 is not set -+# CONFIG_SENSORS_LM85 is not set -+# CONFIG_SENSORS_LM87 is not set -+# CONFIG_SENSORS_LM90 is not set -+# CONFIG_SENSORS_LM92 is not set -+# CONFIG_SENSORS_LM93 is not set -+# CONFIG_SENSORS_LM95234 is not set -+# CONFIG_SENSORS_LM95241 is not set -+# CONFIG_SENSORS_LM95245 is not set -+# CONFIG_SENSORS_PC87360 is not set -+# CONFIG_SENSORS_PC87427 is not set -+# CONFIG_SENSORS_NTC_THERMISTOR is not set -+# CONFIG_SENSORS_NCT6683 is not set -+# CONFIG_SENSORS_NCT6775 is not set -+# CONFIG_SENSORS_NCT7802 is not set -+# CONFIG_SENSORS_NCT7904 is not set -+# CONFIG_SENSORS_NPCM7XX is not set -+# CONFIG_SENSORS_OCC_P8_I2C is not set -+# CONFIG_SENSORS_PCF8591 is not set -+# CONFIG_PMBUS is not set - CONFIG_SENSORS_PWM_FAN=m -+# CONFIG_SENSORS_SHT15 is not set -+# CONFIG_SENSORS_SHT21 is not set -+# CONFIG_SENSORS_SHT3x is not set -+# CONFIG_SENSORS_SHTC1 is not set -+# CONFIG_SENSORS_SIS5595 is not set -+# CONFIG_SENSORS_DME1737 is not set -+# CONFIG_SENSORS_EMC1403 is not set -+# CONFIG_SENSORS_EMC2103 is not set -+# CONFIG_SENSORS_EMC6W201 is not set -+# CONFIG_SENSORS_SMSC47M1 is not set -+# CONFIG_SENSORS_SMSC47M192 is not set -+# CONFIG_SENSORS_SMSC47B397 is not set -+# CONFIG_SENSORS_SCH5627 is not set -+# CONFIG_SENSORS_SCH5636 is not set -+# CONFIG_SENSORS_STTS751 is not set -+# CONFIG_SENSORS_SMM665 is not set -+# CONFIG_SENSORS_ADC128D818 is not set -+# CONFIG_SENSORS_ADS7828 is not set -+# CONFIG_SENSORS_ADS7871 is not set -+# CONFIG_SENSORS_AMC6821 is not set -+# CONFIG_SENSORS_INA209 is not set - CONFIG_SENSORS_INA2XX=m - CONFIG_SENSORS_INA3221=m -+# CONFIG_SENSORS_TC74 is not set -+# CONFIG_SENSORS_THMC50 is not set -+# CONFIG_SENSORS_TMP102 is not set -+# CONFIG_SENSORS_TMP103 is not set -+# CONFIG_SENSORS_TMP108 is not set -+# CONFIG_SENSORS_TMP401 is not set -+# CONFIG_SENSORS_TMP421 is not set -+# CONFIG_SENSORS_TMP513 is not set -+# CONFIG_SENSORS_VEXPRESS is not set -+# CONFIG_SENSORS_VIA686A is not set -+# CONFIG_SENSORS_VT1211 is not set -+# CONFIG_SENSORS_VT8231 is not set -+# CONFIG_SENSORS_W83773G is not set -+# CONFIG_SENSORS_W83781D is not set -+# CONFIG_SENSORS_W83791D is not set -+# CONFIG_SENSORS_W83792D is not set -+# CONFIG_SENSORS_W83793 is not set -+# CONFIG_SENSORS_W83795 is not set -+# CONFIG_SENSORS_W83L785TS is not set -+# CONFIG_SENSORS_W83L786NG is not set -+# CONFIG_SENSORS_W83627HF is not set -+# CONFIG_SENSORS_W83627EHF is not set -+# CONFIG_SENSORS_XGENE is not set - CONFIG_GPIO_TACHOMETER=y -+ -+# -+# ACPI drivers -+# -+# CONFIG_SENSORS_ACPI_POWER is not set -+ -+# -+# HWMON devices -+# -+CONFIG_SENSORS_F75308=m -+# end of HWMON devices -+ -+CONFIG_THERMAL=y -+# CONFIG_THERMAL_NETLINK is not set -+# CONFIG_THERMAL_STATISTICS is not set -+CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 -+CONFIG_THERMAL_HWMON=y -+CONFIG_THERMAL_OF=y - CONFIG_THERMAL_WRITABLE_TRIPS=y -+CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y -+# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set -+# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set -+# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set -+# CONFIG_THERMAL_GOV_FAIR_SHARE is not set -+CONFIG_THERMAL_GOV_STEP_WISE=y -+# CONFIG_THERMAL_GOV_BANG_BANG is not set -+# CONFIG_THERMAL_GOV_USER_SPACE is not set - CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y - CONFIG_CPU_THERMAL=y -+CONFIG_CPU_FREQ_THERMAL=y - CONFIG_DEVFREQ_THERMAL=y - CONFIG_THERMAL_EMULATION=y -+# CONFIG_THERMAL_MMIO is not set - CONFIG_MAX77620_THERMAL=m -+ -+# -+# NVIDIA Tegra thermal drivers -+# - CONFIG_TEGRA_SOCTHERM=y - CONFIG_TEGRA_BPMP_THERMAL=m - CONFIG_TEGRA_AOTAG=y - CONFIG_TEGRA_TJ_THERMAL=y - CONFIG_TEGRA_CORE_CAPS=y - CONFIG_TEGRA_DFLL_CAPS=y -+# end of NVIDIA Tegra thermal drivers -+ -+# CONFIG_GENERIC_ADC_THERMAL is not set -+ -+# -+# Nvidia Thermal Drivers -+# -+# CONFIG_PWM_FAN is not set - CONFIG_THERMAL_GOV_PID=y --CONFIG_USERSPACE_THERM_ALERT=m -+# CONFIG_THERMAL_GOV_CONTINUOUS is not set -+# CONFIG_TEGRA_THERMAL_THROTTLE is not set - CONFIG_TEGRA23X_OC_EVENT=y - CONFIG_TEGRA19X_OC_EVENT=y -+CONFIG_USERSPACE_THERM_ALERT=m -+# end of Nvidia Thermal Drivers -+ - CONFIG_WATCHDOG=y -+CONFIG_WATCHDOG_CORE=y - CONFIG_WATCHDOG_NOWAYOUT=y -+CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y -+CONFIG_WATCHDOG_OPEN_TIMEOUT=0 -+# CONFIG_WATCHDOG_SYSFS is not set -+ -+# -+# Watchdog Pretimeout Governors -+# -+# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set -+ -+# -+# Watchdog Device Drivers -+# -+# CONFIG_SOFT_WATCHDOG is not set -+# CONFIG_SOFT_PLATFORM_WATCHDOG is not set -+# CONFIG_GPIO_WATCHDOG is not set -+# CONFIG_WDAT_WDT is not set -+# CONFIG_XILINX_WATCHDOG is not set -+# CONFIG_ZIIRAVE_WATCHDOG is not set -+# CONFIG_ARM_SP805_WATCHDOG is not set -+# CONFIG_ARM_SBSA_WATCHDOG is not set -+# CONFIG_CADENCE_WATCHDOG is not set -+# CONFIG_DW_WATCHDOG is not set -+# CONFIG_MAX63XX_WATCHDOG is not set - CONFIG_MAX77620_WATCHDOG=y -+# CONFIG_TEGRA_WATCHDOG_LEGACY is not set -+# CONFIG_TEGRA_WATCHDOG is not set - CONFIG_ARM_SMC_WATCHDOG=y -+# CONFIG_ALIM7101_WDT is not set -+# CONFIG_I6300ESB_WDT is not set -+# CONFIG_MEN_A21_WDT is not set -+ -+# -+# PCI-based Watchdog Cards -+# -+# CONFIG_PCIPCWATCHDOG is not set -+# CONFIG_WDTPCI is not set -+ -+# -+# USB-based Watchdog Cards -+# -+# CONFIG_USBPCWATCHDOG is not set - CONFIG_TEGRA21X_WATCHDOG=y - CONFIG_TEGRA18X_WATCHDOG=y - CONFIG_TEGRA_HV_WATCHDOG=y -+CONFIG_SSB_POSSIBLE=y -+CONFIG_SSB=m -+CONFIG_SSB_SPROM=y -+CONFIG_SSB_BLOCKIO=y -+CONFIG_SSB_PCIHOST_POSSIBLE=y -+CONFIG_SSB_PCIHOST=y -+CONFIG_SSB_B43_PCI_BRIDGE=y -+CONFIG_SSB_SDIOHOST_POSSIBLE=y -+CONFIG_SSB_SDIOHOST=y -+CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y -+CONFIG_SSB_DRIVER_PCICORE=y -+# CONFIG_SSB_DRIVER_GPIO is not set -+CONFIG_BCMA_POSSIBLE=y -+CONFIG_BCMA=m -+CONFIG_BCMA_BLOCKIO=y -+CONFIG_BCMA_HOST_PCI_POSSIBLE=y -+CONFIG_BCMA_HOST_PCI=y -+# CONFIG_BCMA_HOST_SOC is not set -+CONFIG_BCMA_DRIVER_PCI=y -+# CONFIG_BCMA_DRIVER_GMAC_CMN is not set -+# CONFIG_BCMA_DRIVER_GPIO is not set -+# CONFIG_BCMA_DEBUG is not set -+ -+# -+# Multifunction device drivers -+# -+CONFIG_MFD_CORE=y -+# CONFIG_MFD_ACT8945A is not set -+# CONFIG_MFD_AS3711 is not set -+# CONFIG_MFD_AS3722 is not set -+# CONFIG_PMIC_ADP5520 is not set -+# CONFIG_MFD_AAT2870_CORE is not set -+# CONFIG_MFD_ATMEL_FLEXCOM is not set -+# CONFIG_MFD_ATMEL_HLCDC is not set -+# CONFIG_MFD_BCM590XX is not set - CONFIG_MFD_BD9571MWV=y -+# CONFIG_MFD_AXP20X_I2C is not set -+# CONFIG_MFD_MADERA is not set -+# CONFIG_PMIC_DA903X is not set -+# CONFIG_MFD_DA9052_SPI is not set -+# CONFIG_MFD_DA9052_I2C is not set -+# CONFIG_MFD_DA9055 is not set -+# CONFIG_MFD_DA9062 is not set -+# CONFIG_MFD_DA9063 is not set -+# CONFIG_MFD_DA9150 is not set -+# CONFIG_MFD_DLN2 is not set -+# CONFIG_MFD_GATEWORKS_GSC is not set -+# CONFIG_MFD_MC13XXX_SPI is not set -+# CONFIG_MFD_MC13XXX_I2C is not set -+# CONFIG_MFD_MP2629 is not set - CONFIG_MFD_HI6421_PMIC=y -+# CONFIG_HTC_PASIC3 is not set -+# CONFIG_HTC_I2CPLD is not set -+# CONFIG_LPC_ICH is not set -+CONFIG_LPC_SCH=m -+# CONFIG_MFD_IQS62X is not set -+# CONFIG_MFD_JANZ_CMODIO is not set -+# CONFIG_MFD_KEMPLD is not set -+# CONFIG_MFD_88PM800 is not set -+# CONFIG_MFD_88PM805 is not set -+# CONFIG_MFD_88PM860X is not set -+# CONFIG_MFD_MAX14577 is not set - CONFIG_MFD_MAX77620=y -+# CONFIG_MFD_MAX77650 is not set -+# CONFIG_MFD_MAX77686 is not set -+# CONFIG_MFD_MAX77693 is not set -+# CONFIG_MFD_MAX77843 is not set -+# CONFIG_MFD_MAX8907 is not set -+# CONFIG_MFD_MAX8925 is not set -+# CONFIG_MFD_MAX8997 is not set -+# CONFIG_MFD_MAX8998 is not set -+# CONFIG_MFD_MT6360 is not set -+# CONFIG_MFD_MT6397 is not set -+# CONFIG_MFD_MENF21BMC is not set -+# CONFIG_EZX_PCAP is not set -+# CONFIG_MFD_CPCAP is not set -+# CONFIG_MFD_VIPERBOARD is not set -+# CONFIG_MFD_RETU is not set -+# CONFIG_MFD_PCF50633 is not set -+# CONFIG_MFD_RDC321X is not set -+# CONFIG_MFD_RT5033 is not set -+# CONFIG_MFD_RC5T583 is not set - CONFIG_MFD_RK808=y -+# CONFIG_MFD_RN5T618 is not set - CONFIG_MFD_SEC_CORE=y -+# CONFIG_MFD_SI476X_CORE is not set -+# CONFIG_MFD_SM501 is not set -+# CONFIG_MFD_SKY81452 is not set -+# CONFIG_ABX500_CORE is not set -+# CONFIG_MFD_STMPE is not set -+CONFIG_MFD_SYSCON=y -+# CONFIG_MFD_TI_AM335X_TSCADC is not set -+# CONFIG_MFD_LP3943 is not set -+# CONFIG_MFD_LP8788 is not set -+# CONFIG_MFD_TI_LMU is not set -+# CONFIG_MFD_PALMAS is not set -+# CONFIG_TPS6105X is not set -+# CONFIG_TPS65010 is not set -+# CONFIG_TPS6507X is not set -+# CONFIG_MFD_TPS65086 is not set -+# CONFIG_MFD_TPS65090 is not set -+# CONFIG_MFD_TPS65217 is not set -+# CONFIG_MFD_TI_LP873X is not set -+# CONFIG_MFD_TI_LP87565 is not set -+# CONFIG_MFD_TPS65218 is not set -+# CONFIG_MFD_TPS6586X is not set -+# CONFIG_MFD_TPS65910 is not set -+# CONFIG_MFD_TPS65912_I2C is not set -+# CONFIG_MFD_TPS65912_SPI is not set -+# CONFIG_MFD_TPS80031 is not set -+# CONFIG_TWL4030_CORE is not set -+# CONFIG_TWL6040_CORE is not set -+# CONFIG_MFD_WL1273_CORE is not set -+# CONFIG_MFD_LM3533 is not set -+# CONFIG_MFD_TC3589X is not set -+# CONFIG_MFD_TQMX86 is not set -+# CONFIG_MFD_VX855 is not set -+# CONFIG_MFD_LOCHNAGAR is not set -+# CONFIG_MFD_ARIZONA_I2C is not set -+# CONFIG_MFD_ARIZONA_SPI is not set -+# CONFIG_MFD_WM8400 is not set -+# CONFIG_MFD_WM831X_I2C is not set -+# CONFIG_MFD_WM831X_SPI is not set -+# CONFIG_MFD_WM8350_I2C is not set -+# CONFIG_MFD_WM8994 is not set - CONFIG_MFD_ROHM_BD718XX=y -+# CONFIG_MFD_ROHM_BD70528 is not set -+# CONFIG_MFD_ROHM_BD71828 is not set -+# CONFIG_MFD_STPMIC1 is not set -+# CONFIG_MFD_STMFX is not set -+CONFIG_MFD_VEXPRESS_SYSREG=y -+# CONFIG_RAVE_SP_CORE is not set -+# CONFIG_MFD_INTEL_M10_BMC is not set -+# end of Multifunction device drivers -+ -+CONFIG_MFD_TMPM32X_I2C=y - CONFIG_MFD_NVVRS_PSEQ=y --CONFIG_NVVRS_PSEQ_RTC=y - CONFIG_REGULATOR=y -+# CONFIG_REGULATOR_DEBUG is not set - CONFIG_REGULATOR_FIXED_VOLTAGE=y -+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set -+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set -+# CONFIG_REGULATOR_88PG86X is not set -+# CONFIG_REGULATOR_ACT8865 is not set -+# CONFIG_REGULATOR_AD5398 is not set -+# CONFIG_REGULATOR_BD718XX is not set -+# CONFIG_REGULATOR_BD9571MWV is not set -+# CONFIG_REGULATOR_DA9210 is not set -+# CONFIG_REGULATOR_DA9211 is not set -+# CONFIG_REGULATOR_FAN53555 is not set -+# CONFIG_REGULATOR_FAN53880 is not set - CONFIG_REGULATOR_GPIO=y -+# CONFIG_REGULATOR_HI6421 is not set -+# CONFIG_REGULATOR_HI6421V530 is not set -+# CONFIG_REGULATOR_ISL9305 is not set -+# CONFIG_REGULATOR_ISL6271A is not set -+# CONFIG_REGULATOR_LP3971 is not set -+# CONFIG_REGULATOR_LP3972 is not set -+# CONFIG_REGULATOR_LP872X is not set -+# CONFIG_REGULATOR_LP8755 is not set -+# CONFIG_REGULATOR_LTC3589 is not set -+# CONFIG_REGULATOR_LTC3676 is not set -+# CONFIG_REGULATOR_MAX1586 is not set - CONFIG_REGULATOR_MAX77620=y -+# CONFIG_REGULATOR_MAX16989 is not set -+# CONFIG_REGULATOR_MAX8649 is not set -+# CONFIG_REGULATOR_MAX8660 is not set -+# CONFIG_REGULATOR_MAX8952 is not set -+# CONFIG_REGULATOR_MAX8973 is not set -+# CONFIG_REGULATOR_MAX77812 is not set -+# CONFIG_REGULATOR_MAX77826 is not set -+# CONFIG_REGULATOR_MCP16502 is not set -+# CONFIG_REGULATOR_MP5416 is not set -+# CONFIG_REGULATOR_MP8859 is not set -+# CONFIG_REGULATOR_MP886X is not set -+# CONFIG_REGULATOR_MPQ7920 is not set -+# CONFIG_REGULATOR_MT6311 is not set -+# CONFIG_REGULATOR_PCA9450 is not set -+# CONFIG_REGULATOR_PFUZE100 is not set -+# CONFIG_REGULATOR_PV88060 is not set -+# CONFIG_REGULATOR_PV88080 is not set -+# CONFIG_REGULATOR_PV88090 is not set - CONFIG_REGULATOR_PWM=y -+# CONFIG_REGULATOR_QCOM_SPMI is not set -+# CONFIG_REGULATOR_QCOM_USB_VBUS is not set -+# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set -+# CONFIG_REGULATOR_RK808 is not set -+# CONFIG_REGULATOR_RT4801 is not set -+# CONFIG_REGULATOR_RTMV20 is not set -+# CONFIG_REGULATOR_S2MPA01 is not set -+# CONFIG_REGULATOR_S2MPS11 is not set -+# CONFIG_REGULATOR_S5M8767 is not set -+# CONFIG_REGULATOR_SLG51000 is not set -+# CONFIG_REGULATOR_SY8106A is not set -+# CONFIG_REGULATOR_SY8824X is not set -+# CONFIG_REGULATOR_SY8827N is not set -+# CONFIG_REGULATOR_TPS51632 is not set -+# CONFIG_REGULATOR_TPS62360 is not set -+# CONFIG_REGULATOR_TPS65023 is not set -+# CONFIG_REGULATOR_TPS6507X is not set - CONFIG_REGULATOR_TPS65132=y -+# CONFIG_REGULATOR_TPS6524X is not set -+# CONFIG_REGULATOR_TPS61280 is not set -+# CONFIG_REGULATOR_VCTRL is not set -+# CONFIG_REGULATOR_VEXPRESS is not set -+# CONFIG_REGULATOR_QCOM_LABIBB is not set - CONFIG_REGULATOR_PMIC_OTP=y - CONFIG_REGULATOR_NCP81599=y - CONFIG_RC_CORE=y -+CONFIG_RC_MAP=y -+# CONFIG_LIRC is not set -+# CONFIG_RC_DECODERS is not set -+# CONFIG_RC_DEVICES is not set -+# CONFIG_MEDIA_CEC_SUPPORT is not set - CONFIG_MEDIA_SUPPORT=y - CONFIG_MEDIA_SUPPORT_FILTER=y - # CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set -+ -+# -+# Media device types -+# - CONFIG_MEDIA_CAMERA_SUPPORT=y -+# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set - CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y -+# CONFIG_MEDIA_RADIO_SUPPORT is not set -+# CONFIG_MEDIA_SDR_SUPPORT is not set - CONFIG_MEDIA_PLATFORM_SUPPORT=y - CONFIG_MEDIA_TEST_SUPPORT=y -+# end of Media device types -+ -+CONFIG_VIDEO_DEV=y -+CONFIG_MEDIA_CONTROLLER=y -+CONFIG_DVB_CORE=y -+ -+# -+# Video4Linux options -+# -+CONFIG_VIDEO_V4L2=y -+CONFIG_VIDEO_V4L2_I2C=y - CONFIG_VIDEO_V4L2_SUBDEV_API=y -+# CONFIG_VIDEO_ADV_DEBUG is not set -+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set -+CONFIG_V4L2_FWNODE=y -+CONFIG_VIDEOBUF_GEN=m -+CONFIG_VIDEOBUF_VMALLOC=m -+# end of Video4Linux options -+ -+# -+# Media controller options -+# -+# CONFIG_MEDIA_CONTROLLER_DVB is not set -+# end of Media controller options -+ -+# -+# Digital TV options -+# -+# CONFIG_DVB_MMAP is not set -+CONFIG_DVB_NET=y -+CONFIG_DVB_MAX_ADAPTERS=16 - # CONFIG_DVB_DYNAMIC_MINORS is not set -+# CONFIG_DVB_DEMUX_SECTION_LOSS_LOG is not set -+# CONFIG_DVB_ULE_DEBUG is not set -+# end of Digital TV options -+ -+# -+# Media drivers -+# -+ -+# -+# Drivers filtered as selected at 'Filter media drivers' -+# - CONFIG_MEDIA_USB_SUPPORT=y -+ -+# -+# Webcam devices -+# - CONFIG_USB_VIDEO_CLASS=m -+CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y -+CONFIG_USB_GSPCA=m - CONFIG_USB_M5602=m - CONFIG_USB_STV06XX=m - CONFIG_USB_GL860=m -@@ -872,20 +4505,265 @@ CONFIG_USB_GSPCA_VICAM=m - CONFIG_USB_GSPCA_XIRLINK_CIT=m - CONFIG_USB_GSPCA_ZC3XX=m - CONFIG_USB_PWC=m -+# CONFIG_USB_PWC_DEBUG is not set -+CONFIG_USB_PWC_INPUT_EVDEV=y - CONFIG_VIDEO_CPIA2=m - CONFIG_USB_ZR364XX=m - CONFIG_USB_STKWEBCAM=m - CONFIG_USB_S2255=m - CONFIG_VIDEO_USBTV=m -+ -+# -+# Analog/digital TV USB devices -+# -+# CONFIG_VIDEO_AU0828 is not set -+# CONFIG_VIDEO_CX231XX is not set -+# CONFIG_VIDEO_TM6000 is not set -+ -+# -+# Digital TV USB devices -+# -+# CONFIG_DVB_USB is not set -+# CONFIG_DVB_USB_V2 is not set -+# CONFIG_DVB_TTUSB_BUDGET is not set -+# CONFIG_DVB_TTUSB_DEC is not set -+# CONFIG_SMS_USB_DRV is not set -+# CONFIG_DVB_B2C2_FLEXCOP_USB is not set -+# CONFIG_DVB_AS102 is not set -+ -+# -+# Webcam, TV (analog/digital) USB devices -+# -+# CONFIG_VIDEO_EM28XX is not set -+# CONFIG_MEDIA_PCI_SUPPORT is not set -+CONFIG_VIDEOBUF2_CORE=y -+CONFIG_VIDEOBUF2_V4L2=y -+CONFIG_VIDEOBUF2_MEMOPS=y -+CONFIG_VIDEOBUF2_DMA_CONTIG=y -+CONFIG_VIDEOBUF2_VMALLOC=m - CONFIG_V4L_PLATFORM_DRIVERS=y -+# CONFIG_VIDEO_CAFE_CCIC is not set -+# CONFIG_VIDEO_CADENCE is not set -+# CONFIG_VIDEO_ASPEED is not set -+# CONFIG_VIDEO_MUX is not set -+# CONFIG_VIDEO_XILINX is not set -+# CONFIG_V4L_MEM2MEM_DRIVERS is not set -+# CONFIG_DVB_PLATFORM_DRIVERS is not set -+ -+# -+# NVIDIA overlay V4L platform devices -+# -+CONFIG_TEGRA_MIPI_CAL=y - CONFIG_VIDEO_CAMERA=y -+CONFIG_VIDEO_TEGRA_VI=y -+CONFIG_VIDEO_TEGRA_VI_TPG=m -+CONFIG_VIDEO_CAMERA_SKT=m - CONFIG_VIDEO_ISC=m - CONFIG_VIDEO_CDI=m -+# CONFIG_VIDEO_TEGRA_VIVID is not set -+# end of NVIDIA overlay V4L platform devices -+ -+# -+# MMC/SDIO DVB adapters -+# -+# CONFIG_SMS_SDIO_DRV is not set - CONFIG_V4L_TEST_DRIVERS=y --CONFIG_VIDEO_ECAM=m -+# CONFIG_VIDEO_VIMC is not set -+# CONFIG_VIDEO_VIVID is not set -+# CONFIG_VIDEO_VIM2M is not set -+# CONFIG_VIDEO_VICODEC is not set -+# CONFIG_DVB_TEST_DRIVERS is not set -+# end of Media drivers -+ -+# -+# Media ancillary drivers -+# -+CONFIG_MEDIA_ATTACH=y -+CONFIG_VIDEO_IR_I2C=y -+ -+# -+# Audio decoders, processors and mixers -+# -+# CONFIG_VIDEO_TVAUDIO is not set -+# CONFIG_VIDEO_TDA7432 is not set -+# CONFIG_VIDEO_TDA9840 is not set -+# CONFIG_VIDEO_TDA1997X is not set -+# CONFIG_VIDEO_TEA6415C is not set -+# CONFIG_VIDEO_TEA6420 is not set -+# CONFIG_VIDEO_MSP3400 is not set -+# CONFIG_VIDEO_CS3308 is not set -+# CONFIG_VIDEO_CS5345 is not set -+# CONFIG_VIDEO_CS53L32A is not set -+# CONFIG_VIDEO_TLV320AIC23B is not set -+# CONFIG_VIDEO_UDA1342 is not set -+# CONFIG_VIDEO_WM8775 is not set -+# CONFIG_VIDEO_WM8739 is not set -+# CONFIG_VIDEO_VP27SMPX is not set -+# CONFIG_VIDEO_SONY_BTF_MPX is not set -+# end of Audio decoders, processors and mixers -+ -+# -+# RDS decoders -+# -+# CONFIG_VIDEO_SAA6588 is not set -+# end of RDS decoders -+ -+# -+# Video decoders -+# -+# CONFIG_VIDEO_ADV7180 is not set -+# CONFIG_VIDEO_ADV7183 is not set -+# CONFIG_VIDEO_ADV748X is not set -+# CONFIG_VIDEO_ADV7604 is not set -+# CONFIG_VIDEO_ADV7842 is not set -+# CONFIG_VIDEO_BT819 is not set -+# CONFIG_VIDEO_BT856 is not set -+# CONFIG_VIDEO_BT866 is not set -+# CONFIG_VIDEO_KS0127 is not set -+# CONFIG_VIDEO_ML86V7667 is not set -+# CONFIG_VIDEO_SAA7110 is not set -+# CONFIG_VIDEO_SAA711X is not set -+# CONFIG_VIDEO_TC358743 is not set -+# CONFIG_VIDEO_TVP514X is not set -+# CONFIG_VIDEO_TVP5150 is not set -+# CONFIG_VIDEO_TVP7002 is not set -+# CONFIG_VIDEO_TW2804 is not set -+# CONFIG_VIDEO_TW9903 is not set -+# CONFIG_VIDEO_TW9906 is not set -+# CONFIG_VIDEO_TW9910 is not set -+# CONFIG_VIDEO_VPX3220 is not set -+# CONFIG_VIDEO_MAX9286 is not set -+ -+# -+# Video and audio decoders -+# -+# CONFIG_VIDEO_SAA717X is not set -+# CONFIG_VIDEO_CX25840 is not set -+# end of Video decoders -+ -+# -+# Video encoders -+# -+# CONFIG_VIDEO_SAA7127 is not set -+# CONFIG_VIDEO_SAA7185 is not set -+# CONFIG_VIDEO_ADV7170 is not set -+# CONFIG_VIDEO_ADV7175 is not set -+# CONFIG_VIDEO_ADV7343 is not set -+# CONFIG_VIDEO_ADV7393 is not set -+# CONFIG_VIDEO_ADV7511 is not set -+# CONFIG_VIDEO_AD9389B is not set -+# CONFIG_VIDEO_AK881X is not set -+# CONFIG_VIDEO_THS8200 is not set -+# end of Video encoders -+ -+# -+# Video improvement chips -+# -+# CONFIG_VIDEO_UPD64031A is not set -+# CONFIG_VIDEO_UPD64083 is not set -+# end of Video improvement chips -+ -+# -+# Audio/Video compression chips -+# -+# CONFIG_VIDEO_SAA6752HS is not set -+# end of Audio/Video compression chips -+ -+# -+# SDR tuner chips -+# -+# end of SDR tuner chips -+ -+# -+# Miscellaneous helper chips -+# -+# CONFIG_VIDEO_THS7303 is not set -+# CONFIG_VIDEO_M52790 is not set -+# CONFIG_VIDEO_I2C is not set -+# CONFIG_VIDEO_ST_MIPID02 is not set -+# end of Miscellaneous helper chips -+ -+# -+# Camera sensor devices -+# -+# CONFIG_VIDEO_HI556 is not set -+# CONFIG_VIDEO_IMX214 is not set -+# CONFIG_VIDEO_IMX219 is not set -+# CONFIG_VIDEO_IMX258 is not set -+# CONFIG_VIDEO_IMX274 is not set -+# CONFIG_VIDEO_IMX290 is not set -+# CONFIG_VIDEO_IMX319 is not set -+# CONFIG_VIDEO_IMX355 is not set -+# CONFIG_VIDEO_OV2640 is not set -+# CONFIG_VIDEO_OV2659 is not set -+# CONFIG_VIDEO_OV2680 is not set -+# CONFIG_VIDEO_OV2685 is not set -+# CONFIG_VIDEO_OV2740 is not set -+# CONFIG_VIDEO_OV5640 is not set -+# CONFIG_VIDEO_OV5645 is not set -+# CONFIG_VIDEO_OV5647 is not set -+# CONFIG_VIDEO_OV6650 is not set -+# CONFIG_VIDEO_OV5670 is not set -+# CONFIG_VIDEO_OV5675 is not set -+# CONFIG_VIDEO_OV5695 is not set -+# CONFIG_VIDEO_OV7251 is not set -+# CONFIG_VIDEO_OV772X is not set -+# CONFIG_VIDEO_OV7640 is not set -+# CONFIG_VIDEO_OV7670 is not set -+# CONFIG_VIDEO_OV7740 is not set -+# CONFIG_VIDEO_OV8856 is not set -+# CONFIG_VIDEO_OV9640 is not set -+# CONFIG_VIDEO_OV9650 is not set -+# CONFIG_VIDEO_OV13858 is not set -+# CONFIG_VIDEO_VS6624 is not set -+# CONFIG_VIDEO_MT9M001 is not set -+# CONFIG_VIDEO_MT9M032 is not set -+# CONFIG_VIDEO_MT9M111 is not set -+# CONFIG_VIDEO_MT9P031 is not set -+# CONFIG_VIDEO_MT9T001 is not set -+# CONFIG_VIDEO_MT9T112 is not set -+# CONFIG_VIDEO_MT9V011 is not set -+# CONFIG_VIDEO_MT9V032 is not set -+# CONFIG_VIDEO_MT9V111 is not set -+# CONFIG_VIDEO_SR030PC30 is not set -+# CONFIG_VIDEO_NOON010PC30 is not set -+# CONFIG_VIDEO_M5MOLS is not set -+# CONFIG_VIDEO_RDACM20 is not set -+# CONFIG_VIDEO_RJ54N1 is not set -+# CONFIG_VIDEO_S5K6AA is not set -+# CONFIG_VIDEO_S5K6A3 is not set -+# CONFIG_VIDEO_S5K4ECGX is not set -+# CONFIG_VIDEO_S5K5BAF is not set -+# CONFIG_VIDEO_SMIAPP is not set -+# CONFIG_VIDEO_ET8EK8 is not set -+# CONFIG_VIDEO_S5C73M3 is not set -+# end of Camera sensor devices -+ -+# -+# Lens drivers -+# -+# CONFIG_VIDEO_AD5820 is not set -+# CONFIG_VIDEO_AK7375 is not set -+# CONFIG_VIDEO_DW9714 is not set -+# CONFIG_VIDEO_DW9768 is not set -+# CONFIG_VIDEO_DW9807_VCM is not set -+# end of Lens drivers -+ -+# -+# Flash devices -+# -+# CONFIG_VIDEO_ADP1653 is not set -+# CONFIG_VIDEO_LM3560 is not set -+# CONFIG_VIDEO_LM3646 is not set -+# end of Flash devices -+ -+# -+# NVIDIA overlay Encoders, decoders, sensors and other helper chips -+# - CONFIG_NV_VIDEO_IMX185=m --CONFIG_NV_VIDEO_IMX219=m - CONFIG_NV_VIDEO_IMX477=m -+CONFIG_VIDEO_ECAM=m -+CONFIG_NV_VIDEO_IMX219=m - CONFIG_NV_VIDEO_IMX268=m - CONFIG_NV_VIDEO_IMX274=m - CONFIG_NV_VIDEO_IMX318=m -@@ -903,16 +4781,55 @@ CONFIG_NV_VIDEO_IMX390=y - CONFIG_NV_DESER_MAX96712=m - CONFIG_NV_VIDEO_AR0234=m - CONFIG_NV_VIDEO_HAWK_OWL=m -+# end of NVIDIA overlay Encoders, decoders, sensors and other helper chips -+ -+# -+# SPI helper chips -+# -+# CONFIG_VIDEO_GS1662 is not set -+# end of SPI helper chips -+ -+# -+# Media SPI Adapters -+# - # CONFIG_CXD2880_SPI_DRV is not set -+# end of Media SPI Adapters -+ -+# CONFIG_VIDEO_IMX204 is not set -+CONFIG_MEDIA_TUNER=y -+ -+# -+# Customize TV tuners -+# - CONFIG_MEDIA_TUNER_SIMPLE=y - # CONFIG_MEDIA_TUNER_TDA18250 is not set - CONFIG_MEDIA_TUNER_TDA8290=y -+CONFIG_MEDIA_TUNER_TDA827X=y -+CONFIG_MEDIA_TUNER_TDA18271=y - # CONFIG_MEDIA_TUNER_TDA18272 is not set -+CONFIG_MEDIA_TUNER_TDA9887=y -+CONFIG_MEDIA_TUNER_TEA5761=m -+CONFIG_MEDIA_TUNER_TEA5767=m -+CONFIG_MEDIA_TUNER_MSI001=m - CONFIG_MEDIA_TUNER_MT20XX=y -+CONFIG_MEDIA_TUNER_MT2060=m -+CONFIG_MEDIA_TUNER_MT2063=m -+CONFIG_MEDIA_TUNER_MT2266=m -+CONFIG_MEDIA_TUNER_MT2131=m -+CONFIG_MEDIA_TUNER_QT1010=m - CONFIG_MEDIA_TUNER_XC2028=y - CONFIG_MEDIA_TUNER_XC5000=y - CONFIG_MEDIA_TUNER_XC4000=y -+CONFIG_MEDIA_TUNER_MXL5005S=m -+CONFIG_MEDIA_TUNER_MXL5007T=m - CONFIG_MEDIA_TUNER_MC44S803=y -+CONFIG_MEDIA_TUNER_MAX2165=m -+CONFIG_MEDIA_TUNER_TDA18218=m -+CONFIG_MEDIA_TUNER_FC0011=m -+CONFIG_MEDIA_TUNER_FC0012=m -+CONFIG_MEDIA_TUNER_FC0013=m -+CONFIG_MEDIA_TUNER_TDA18212=m -+CONFIG_MEDIA_TUNER_E4000=m - # CONFIG_MEDIA_TUNER_FC2580 is not set - # CONFIG_MEDIA_TUNER_M88RS6000T is not set - # CONFIG_MEDIA_TUNER_TUA9001 is not set -@@ -922,51 +4839,716 @@ CONFIG_MEDIA_TUNER_MC44S803=y - # CONFIG_MEDIA_TUNER_MXL301RF is not set - # CONFIG_MEDIA_TUNER_QM1D1C0042 is not set - # CONFIG_MEDIA_TUNER_QM1D1B0004 is not set -+# end of Customize TV tuners -+ -+# -+# Nvidia overlay Customize TV tuners -+# -+# end of Nvidia overlay Customize TV tuners -+ -+# -+# Customise DVB Frontends -+# -+ -+# -+# Multistandard (satellite) frontends -+# -+CONFIG_DVB_STB0899=m -+CONFIG_DVB_STB6100=m -+CONFIG_DVB_STV090x=m -+CONFIG_DVB_STV0910=m -+CONFIG_DVB_STV6110x=m -+CONFIG_DVB_STV6111=m -+CONFIG_DVB_MXL5XX=m -+CONFIG_DVB_M88DS3103=m -+ -+# -+# Multistandard (cable + terrestrial) frontends -+# -+CONFIG_DVB_DRXK=m -+CONFIG_DVB_TDA18271C2DD=m -+CONFIG_DVB_SI2165=m -+CONFIG_DVB_MN88472=m -+CONFIG_DVB_MN88473=m -+ -+# -+# DVB-S (satellite) frontends -+# -+CONFIG_DVB_CX24110=m -+CONFIG_DVB_CX24123=m -+CONFIG_DVB_MT312=m -+CONFIG_DVB_ZL10036=m -+CONFIG_DVB_ZL10039=m -+CONFIG_DVB_S5H1420=m -+CONFIG_DVB_STV0288=m -+CONFIG_DVB_STB6000=m -+CONFIG_DVB_STV0299=m -+CONFIG_DVB_STV6110=m -+CONFIG_DVB_STV0900=m -+CONFIG_DVB_TDA8083=m -+CONFIG_DVB_TDA10086=m -+CONFIG_DVB_TDA8261=m -+CONFIG_DVB_VES1X93=m -+CONFIG_DVB_TUNER_ITD1000=m -+CONFIG_DVB_TUNER_CX24113=m -+CONFIG_DVB_TDA826X=m -+CONFIG_DVB_TUA6100=m -+CONFIG_DVB_CX24116=m -+CONFIG_DVB_CX24117=m -+CONFIG_DVB_CX24120=m -+CONFIG_DVB_SI21XX=m -+CONFIG_DVB_TS2020=m -+CONFIG_DVB_DS3000=m -+CONFIG_DVB_MB86A16=m -+CONFIG_DVB_TDA10071=m -+ -+# -+# DVB-T (terrestrial) frontends -+# -+CONFIG_DVB_SP8870=m -+CONFIG_DVB_SP887X=m -+CONFIG_DVB_CX22700=m -+CONFIG_DVB_CX22702=m -+CONFIG_DVB_S5H1432=m -+CONFIG_DVB_DRXD=m -+CONFIG_DVB_L64781=m -+CONFIG_DVB_TDA1004X=m -+CONFIG_DVB_NXT6000=m -+CONFIG_DVB_MT352=m -+CONFIG_DVB_ZL10353=m -+CONFIG_DVB_DIB3000MB=m -+CONFIG_DVB_DIB3000MC=m -+CONFIG_DVB_DIB7000M=m -+CONFIG_DVB_DIB7000P=m -+CONFIG_DVB_DIB9000=m -+CONFIG_DVB_TDA10048=m -+CONFIG_DVB_AF9013=m -+CONFIG_DVB_EC100=m -+CONFIG_DVB_STV0367=m -+CONFIG_DVB_CXD2820R=m -+CONFIG_DVB_CXD2841ER=m -+CONFIG_DVB_RTL2830=m -+CONFIG_DVB_RTL2832=m -+CONFIG_DVB_SI2168=m -+CONFIG_DVB_ZD1301_DEMOD=m - # CONFIG_DVB_CXD2880 is not set -+ -+# -+# DVB-C (cable) frontends -+# -+CONFIG_DVB_VES1820=m -+CONFIG_DVB_TDA10021=m -+CONFIG_DVB_TDA10023=m -+CONFIG_DVB_STV0297=m -+ -+# -+# ATSC (North American/Korean Terrestrial/Cable DTV) frontends -+# -+CONFIG_DVB_NXT200X=m -+CONFIG_DVB_OR51211=m -+CONFIG_DVB_OR51132=m -+CONFIG_DVB_BCM3510=m -+CONFIG_DVB_LGDT330X=m -+CONFIG_DVB_LGDT3305=m -+CONFIG_DVB_LGDT3306A=m -+CONFIG_DVB_LG2160=m -+CONFIG_DVB_S5H1409=m -+CONFIG_DVB_AU8522=m -+CONFIG_DVB_AU8522_DTV=m -+CONFIG_DVB_AU8522_V4L=m -+CONFIG_DVB_S5H1411=m -+ -+# -+# ISDB-T (terrestrial) frontends -+# -+CONFIG_DVB_S921=m -+CONFIG_DVB_DIB8000=m -+CONFIG_DVB_MB86A20S=m -+ -+# -+# ISDB-S (satellite) & ISDB-T (terrestrial) frontends -+# -+CONFIG_DVB_TC90522=m - # CONFIG_DVB_MN88443X is not set -+ -+# -+# Digital terrestrial only tuners/PLL -+# -+CONFIG_DVB_PLL=m -+CONFIG_DVB_TUNER_DIB0070=m -+CONFIG_DVB_TUNER_DIB0090=m -+ -+# -+# SEC control devices for DVB-S -+# -+CONFIG_DVB_DRX39XYJ=m -+CONFIG_DVB_LNBH25=m - # CONFIG_DVB_LNBH29 is not set -+CONFIG_DVB_LNBP21=m -+CONFIG_DVB_LNBP22=m -+CONFIG_DVB_ISL6405=m -+CONFIG_DVB_ISL6421=m -+CONFIG_DVB_ISL6423=m -+CONFIG_DVB_A8293=m -+CONFIG_DVB_LGS8GL5=m -+CONFIG_DVB_LGS8GXX=m -+CONFIG_DVB_ATBM8830=m -+CONFIG_DVB_TDA665x=m -+CONFIG_DVB_IX2505V=m -+CONFIG_DVB_M88RS2000=m -+CONFIG_DVB_AF9033=m -+CONFIG_DVB_HORUS3A=m -+CONFIG_DVB_ASCOT2E=m -+CONFIG_DVB_HELENE=m -+ -+# -+# Common Interface (EN50221) controller drivers -+# - # CONFIG_DVB_CXD2099 is not set -+CONFIG_DVB_SP2=m -+# end of Customise DVB Frontends -+ -+# -+# Tools to develop new frontends -+# -+# CONFIG_DVB_DUMMY_FE is not set -+# end of Media ancillary drivers -+ -+# -+# Graphics support -+# - # CONFIG_VGA_ARB is not set -+# CONFIG_TEGRA_HOST1X is not set - CONFIG_DRM=y -+CONFIG_DRM_MIPI_DSI=y -+# CONFIG_DRM_DP_AUX_CHARDEV is not set -+# CONFIG_DRM_DEBUG_MM is not set -+# CONFIG_DRM_DEBUG_SELFTEST is not set -+CONFIG_DRM_KMS_HELPER=y -+CONFIG_DRM_KMS_FB_HELPER=y -+# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set -+CONFIG_DRM_FBDEV_EMULATION=y -+CONFIG_DRM_FBDEV_OVERALLOC=100 -+# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set -+# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set -+# CONFIG_DRM_DP_CEC is not set -+CONFIG_DRM_TTM=m -+CONFIG_DRM_TTM_DMA_PAGE_POOL=y -+CONFIG_DRM_VRAM_HELPER=m -+CONFIG_DRM_TTM_HELPER=m -+CONFIG_DRM_GEM_CMA_HELPER=y -+CONFIG_DRM_KMS_CMA_HELPER=y -+ -+# -+# I2C encoder or helper chips -+# -+# CONFIG_DRM_I2C_CH7006 is not set -+# CONFIG_DRM_I2C_SIL164 is not set - CONFIG_DRM_I2C_NXP_TDA998X=m -+# CONFIG_DRM_I2C_NXP_TDA9950 is not set -+# end of I2C encoder or helper chips -+ -+# -+# ARM devices -+# -+# CONFIG_DRM_HDLCD is not set -+# CONFIG_DRM_MALI_DISPLAY is not set -+# CONFIG_DRM_KOMEDA is not set -+# end of ARM devices -+ -+# CONFIG_DRM_RADEON is not set -+# CONFIG_DRM_AMDGPU is not set -+# CONFIG_DRM_NOUVEAU is not set -+# CONFIG_DRM_VGEM is not set -+# CONFIG_DRM_VKMS is not set -+# CONFIG_DRM_UDL is not set -+# CONFIG_DRM_AST is not set -+# CONFIG_DRM_MGAG200 is not set - CONFIG_DRM_RCAR_DW_HDMI=m - CONFIG_DRM_RCAR_LVDS=m -+# CONFIG_DRM_QXL is not set -+# CONFIG_DRM_BOCHS is not set -+# CONFIG_DRM_VIRTIO_GPU is not set -+# CONFIG_DRM_TEGRA is not set -+CONFIG_DRM_PANEL=y -+ -+# -+# Display Panels -+# -+# CONFIG_DRM_PANEL_ARM_VERSATILE is not set -+# CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 is not set -+# CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set -+# CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set - CONFIG_DRM_PANEL_LVDS=m - CONFIG_DRM_PANEL_SIMPLE=m -+# CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set -+# CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set -+# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set -+# CONFIG_DRM_PANEL_ILITEK_IL9322 is not set -+# CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set -+# CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set -+# CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set -+# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set -+# CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W is not set -+# CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829 is not set -+# CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set -+# CONFIG_DRM_PANEL_LG_LB035Q02 is not set -+# CONFIG_DRM_PANEL_LG_LG4573 is not set -+# CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set -+# CONFIG_DRM_PANEL_NOVATEK_NT35510 is not set -+# CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set -+# CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set -+# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set -+# CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set -+# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set -+# CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set -+# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set -+# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set -+# CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set -+# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set -+# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set -+# CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set -+# CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set -+# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set -+# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set -+# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set -+# CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set -+# CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set -+# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set -+# CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set -+# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set -+# CONFIG_DRM_PANEL_SITRONIX_ST7703 is not set -+# CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set -+# CONFIG_DRM_PANEL_SONY_ACX424AKP is not set -+# CONFIG_DRM_PANEL_SONY_ACX565AKM is not set -+# CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set -+# CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set -+# CONFIG_DRM_PANEL_TPO_TPG110 is not set -+# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set -+# CONFIG_DRM_PANEL_VISIONOX_RM69299 is not set -+# CONFIG_DRM_PANEL_XINPENG_XPP055C272 is not set -+# end of Display Panels -+ -+CONFIG_DRM_BRIDGE=y -+CONFIG_DRM_PANEL_BRIDGE=y -+ -+# -+# Display Interface Bridges -+# -+# CONFIG_DRM_CDNS_DSI is not set -+# CONFIG_DRM_CHRONTEL_CH7033 is not set -+# CONFIG_DRM_DISPLAY_CONNECTOR is not set -+# CONFIG_DRM_LONTIUM_LT9611 is not set -+# CONFIG_DRM_LVDS_CODEC is not set -+# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set -+# CONFIG_DRM_NWL_MIPI_DSI is not set -+# CONFIG_DRM_NXP_PTN3460 is not set -+# CONFIG_DRM_PARADE_PS8622 is not set -+# CONFIG_DRM_PARADE_PS8640 is not set -+# CONFIG_DRM_SIL_SII8620 is not set - CONFIG_DRM_SII902X=m -+# CONFIG_DRM_SII9234 is not set -+# CONFIG_DRM_SIMPLE_BRIDGE is not set -+# CONFIG_DRM_THINE_THC63LVD1024 is not set -+# CONFIG_DRM_TOSHIBA_TC358762 is not set -+# CONFIG_DRM_TOSHIBA_TC358764 is not set -+# CONFIG_DRM_TOSHIBA_TC358767 is not set -+# CONFIG_DRM_TOSHIBA_TC358768 is not set -+# CONFIG_DRM_TOSHIBA_TC358775 is not set -+# CONFIG_DRM_TI_TFP410 is not set -+# CONFIG_DRM_TI_SN65DSI86 is not set -+# CONFIG_DRM_TI_TPD12S015 is not set -+# CONFIG_DRM_ANALOGIX_ANX6345 is not set -+# CONFIG_DRM_ANALOGIX_ANX78XX is not set -+# CONFIG_DRM_I2C_ADV7511 is not set -+# CONFIG_DRM_CDNS_MHDP8546 is not set -+CONFIG_DRM_DW_HDMI=m -+# CONFIG_DRM_DW_HDMI_AHB_AUDIO is not set -+# CONFIG_DRM_DW_HDMI_I2S_AUDIO is not set -+# CONFIG_DRM_DW_HDMI_CEC is not set -+# end of Display Interface Bridges -+ -+# CONFIG_DRM_ETNAVIV is not set -+# CONFIG_DRM_ARCPGU is not set - CONFIG_DRM_HISI_HIBMC=m - CONFIG_DRM_HISI_KIRIN=m -+# CONFIG_DRM_MXSFB is not set -+# CONFIG_DRM_CIRRUS_QEMU is not set -+# CONFIG_DRM_GM12U320 is not set -+# CONFIG_TINYDRM_HX8357D is not set -+# CONFIG_TINYDRM_ILI9225 is not set -+# CONFIG_TINYDRM_ILI9341 is not set -+# CONFIG_TINYDRM_ILI9486 is not set -+# CONFIG_TINYDRM_MI0283QT is not set -+# CONFIG_TINYDRM_REPAPER is not set -+# CONFIG_TINYDRM_ST7586 is not set -+# CONFIG_TINYDRM_ST7735R is not set -+# CONFIG_DRM_PL111 is not set -+# CONFIG_DRM_LIMA is not set -+# CONFIG_DRM_PANFROST is not set -+# CONFIG_DRM_TIDSS is not set -+# CONFIG_DRM_LEGACY is not set -+CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y - CONFIG_DRM_TEGRA_UDRM=m -+ -+# -+# Frame buffer Devices -+# -+CONFIG_FB_CMDLINE=y -+CONFIG_FB_NOTIFY=y -+CONFIG_FB=y -+# CONFIG_FIRMWARE_EDID is not set -+CONFIG_FB_CFB_FILLRECT=y -+CONFIG_FB_CFB_COPYAREA=y -+CONFIG_FB_CFB_IMAGEBLIT=y -+CONFIG_FB_SYS_FILLRECT=y -+CONFIG_FB_SYS_COPYAREA=y -+CONFIG_FB_SYS_IMAGEBLIT=y -+# CONFIG_FB_FOREIGN_ENDIAN is not set -+CONFIG_FB_SYS_FOPS=y -+CONFIG_FB_DEFERRED_IO=y -+CONFIG_FB_MODE_HELPERS=y - CONFIG_FB_MODE_PIXCLOCK_HZ=y -+# CONFIG_FB_TILEBLITTING is not set -+ -+# -+# Frame buffer hardware drivers -+# -+# CONFIG_FB_CIRRUS is not set -+# CONFIG_FB_PM2 is not set -+# CONFIG_FB_ARMCLCD is not set -+# CONFIG_FB_CYBER2000 is not set -+# CONFIG_FB_ASILIANT is not set -+# CONFIG_FB_IMSTT is not set -+CONFIG_FB_EFI=y -+# CONFIG_FB_OPENCORES is not set -+# CONFIG_FB_S1D13XXX is not set -+# CONFIG_FB_NVIDIA is not set -+# CONFIG_FB_RIVA is not set -+# CONFIG_FB_I740 is not set -+# CONFIG_FB_MATROX is not set -+# CONFIG_FB_RADEON is not set -+# CONFIG_FB_ATY128 is not set -+# CONFIG_FB_ATY is not set -+# CONFIG_FB_S3 is not set -+# CONFIG_FB_SAVAGE is not set -+# CONFIG_FB_SIS is not set -+# CONFIG_FB_NEOMAGIC is not set -+# CONFIG_FB_KYRO is not set -+# CONFIG_FB_3DFX is not set -+# CONFIG_FB_VOODOO1 is not set -+# CONFIG_FB_VT8623 is not set -+# CONFIG_FB_TRIDENT is not set -+# CONFIG_FB_ARK is not set -+# CONFIG_FB_PM3 is not set -+# CONFIG_FB_CARMINE is not set -+# CONFIG_FB_SMSCUFX is not set -+# CONFIG_FB_UDL is not set -+# CONFIG_FB_IBM_GXT4500 is not set -+# CONFIG_FB_VIRTUAL is not set -+# CONFIG_FB_METRONOME is not set -+# CONFIG_FB_MB862XX is not set -+# CONFIG_FB_SIMPLE is not set -+# CONFIG_FB_SSD1307 is not set -+# CONFIG_FB_SM712 is not set -+# end of Frame buffer Devices -+ -+# -+# Backlight & LCD device support -+# - CONFIG_LCD_CLASS_DEVICE=m -+# CONFIG_LCD_L4F00242T03 is not set -+# CONFIG_LCD_LMS283GF05 is not set -+# CONFIG_LCD_LTV350QV is not set -+# CONFIG_LCD_ILI922X is not set -+# CONFIG_LCD_ILI9320 is not set -+# CONFIG_LCD_TDO24M is not set -+# CONFIG_LCD_VGG2432A4 is not set -+# CONFIG_LCD_PLATFORM is not set -+# CONFIG_LCD_AMS369FG06 is not set -+# CONFIG_LCD_LMS501KF03 is not set -+# CONFIG_LCD_HX8357 is not set -+# CONFIG_LCD_OTM3225A is not set -+CONFIG_BACKLIGHT_CLASS_DEVICE=y -+# CONFIG_BACKLIGHT_KTD253 is not set - CONFIG_BACKLIGHT_PWM=y -+# CONFIG_BACKLIGHT_QCOM_WLED is not set -+# CONFIG_BACKLIGHT_ADP8860 is not set -+# CONFIG_BACKLIGHT_ADP8870 is not set -+# CONFIG_BACKLIGHT_LM3630A is not set -+# CONFIG_BACKLIGHT_LM3639 is not set - CONFIG_BACKLIGHT_LP855X=y -+# CONFIG_BACKLIGHT_GPIO is not set -+# CONFIG_BACKLIGHT_LV5207LP is not set -+# CONFIG_BACKLIGHT_BD6107 is not set -+# CONFIG_BACKLIGHT_ARCXCNN is not set -+# CONFIG_BACKLIGHT_LED is not set -+# end of Backlight & LCD device support -+ - CONFIG_TEGRA_GRHOST=y -+CONFIG_TEGRA_GRHOST_ISP=y -+CONFIG_TEGRA_GRHOST_VIC=y -+CONFIG_TEGRA_GRHOST_NVDEC=y -+CONFIG_TEGRA_GRHOST_NVDEC_SECURE=y -+CONFIG_TEGRA_GRHOST_NVENC=y -+CONFIG_TEGRA_GRHOST_NVJPG=y -+CONFIG_TEGRA_GRHOST_OFA=y -+CONFIG_TEGRA_GRHOST_TSEC=y - CONFIG_TEGRA_GRHOST_NVCSI=y -+CONFIG_TEGRA_GRHOST_SCALE=y -+CONFIG_TEGRA_GRHOST_DEFAULT_TIMEOUT=10000 -+CONFIG_TEGRA_GRHOST_SYNC=y -+CONFIG_TEGRA_GRHOST_VHOST=y - CONFIG_TEGRA_GR_VIRTUALIZATION=y -+# CONFIG_NVDEC_BOOTLOADER is not set -+CONFIG_TEGRA_CAMERA_PLATFORM=y -+ -+# -+# NVIDIA Tegra Display Driver options -+# - CONFIG_TEGRA_DC=y -+# CONFIG_TEGRA_NVDISPLAY is not set -+CONFIG_TEGRA_DC_64BIT_SUPPORT=y -+CONFIG_TEGRA_DC_TEMPORAL_DITHER=y -+CONFIG_FB_TEGRA=y - CONFIG_TEGRA_DC_SCREEN_CAPTURE=y - CONFIG_TEGRA_DSI=y -+# CONFIG_TEGRA_DSI2EDP_TC358767 is not set -+# CONFIG_TEGRA_DSI2EDP_SN65DSI86 is not set -+# CONFIG_TEGRA_DSI2LVDS_SN65DSI85 is not set -+# CONFIG_TEGRA_LVDS2FPDL_DS90UB947 is not set -+# CONFIG_TEGRA_DS90UH948Q_DESER is not set - CONFIG_MAXIM_GMSL_DP_SERIALIZER=y -+# CONFIG_TEGRA_EDP2LVDS_PS8625 is not set -+CONFIG_TEGRA_DP=y - CONFIG_TEGRA_HDMI2_0=y -+# CONFIG_TEGRA_HDMI2GMSL_MAX929x is not set -+# CONFIG_TEGRA_HDMI2DSI_TC358870 is not set -+CONFIG_TEGRA_HDA_DC=y -+# CONFIG_TEGRA_HDMI2FPD_DS90UH949 is not set -+# CONFIG_TEGRA_NVSR is not set -+# CONFIG_TEGRA_VRR is not set -+# CONFIG_TEGRA_HDMIVRR is not set - CONFIG_TEGRA_HDMIHDCP=y -+# CONFIG_TEGRA_DEBUG_HDCP is not set - CONFIG_TEGRA_DPHDCP=y -+# CONFIG_TEGRA_DEBUG_DP_HDCP is not set -+# CONFIG_TEGRA_YUV_BYPASS_MODE_FILTER is not set -+CONFIG_TEGRA_DC_FAKE_PANEL_SUPPORT=y - CONFIG_TEGRA_CEC_SUPPORT=y -+CONFIG_TEGRA_T23X_GRHOST=y -+CONFIG_TEGRA_T23X_GRHOST_PVA=y -+CONFIG_TEGRA_GRHOST_NVDLA=y -+CONFIG_TEGRA_T19x_GRHOST_PVA=y -+CONFIG_TEGRA_GRHOST_PVA=y - CONFIG_TEGRA_GRHOST_SLVSEC=y -+CONFIG_TEGRA_GRHOST_CAPTURE_SUPPORT=y -+# CONFIG_TEGRA_GRHOST_LEGACY_PD is not set -+# CONFIG_TEGRA_PVA_V1 is not set -+CONFIG_VIDEOMODE_HELPERS=y -+CONFIG_HDMI=y -+ -+# -+# Console display driver support -+# -+CONFIG_DUMMY_CONSOLE=y -+CONFIG_DUMMY_CONSOLE_COLUMNS=80 -+CONFIG_DUMMY_CONSOLE_ROWS=25 - CONFIG_FRAMEBUFFER_CONSOLE=y -+# CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set -+CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y -+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set -+# CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set -+# end of Console display driver support -+ - CONFIG_LOGO=y - # CONFIG_LOGO_LINUX_MONO is not set - # CONFIG_LOGO_LINUX_VGA16 is not set - # CONFIG_LOGO_LINUX_CLUT224 is not set -+ -+# -+# NVIDIA Tegra Display Driver options -+# -+# end of Graphics support -+ - CONFIG_SOUND=y - CONFIG_SND=y -+CONFIG_SND_TIMER=y -+CONFIG_SND_PCM=y -+CONFIG_SND_PCM_ELD=y -+CONFIG_SND_PCM_IEC958=y -+CONFIG_SND_DMAENGINE_PCM=y -+CONFIG_SND_HWDEP=y -+CONFIG_SND_RAWMIDI=y -+CONFIG_SND_COMPRESS_OFFLOAD=y -+CONFIG_SND_JACK=y -+CONFIG_SND_JACK_INPUT_DEV=y -+# CONFIG_SND_OSSEMUL is not set -+CONFIG_SND_PCM_TIMER=y -+# CONFIG_SND_HRTIMER is not set -+CONFIG_SND_DYNAMIC_MINORS=y -+CONFIG_SND_MAX_CARDS=32 -+CONFIG_SND_SUPPORT_OLD_API=y -+CONFIG_SND_PROC_FS=y -+CONFIG_SND_VERBOSE_PROCFS=y -+# CONFIG_SND_VERBOSE_PRINTK is not set -+# CONFIG_SND_DEBUG is not set -+CONFIG_SND_VMASTER=y -+# CONFIG_SND_SEQUENCER is not set -+CONFIG_SND_DRIVERS=y -+# CONFIG_SND_DUMMY is not set - CONFIG_SND_ALOOP=m -+# CONFIG_SND_MTPAV is not set -+# CONFIG_SND_SERIAL_U16550 is not set -+# CONFIG_SND_MPU401 is not set -+CONFIG_SND_PCI=y -+# CONFIG_SND_AD1889 is not set -+# CONFIG_SND_ALS300 is not set -+# CONFIG_SND_ALI5451 is not set -+# CONFIG_SND_ATIIXP is not set -+# CONFIG_SND_ATIIXP_MODEM is not set -+# CONFIG_SND_AU8810 is not set -+# CONFIG_SND_AU8820 is not set -+# CONFIG_SND_AU8830 is not set -+# CONFIG_SND_AW2 is not set -+# CONFIG_SND_AZT3328 is not set -+# CONFIG_SND_BT87X is not set -+# CONFIG_SND_CA0106 is not set -+# CONFIG_SND_CMIPCI is not set -+# CONFIG_SND_OXYGEN is not set -+# CONFIG_SND_CS4281 is not set -+# CONFIG_SND_CS46XX is not set -+# CONFIG_SND_CTXFI is not set -+# CONFIG_SND_DARLA20 is not set -+# CONFIG_SND_GINA20 is not set -+# CONFIG_SND_LAYLA20 is not set -+# CONFIG_SND_DARLA24 is not set -+# CONFIG_SND_GINA24 is not set -+# CONFIG_SND_LAYLA24 is not set -+# CONFIG_SND_MONA is not set -+# CONFIG_SND_MIA is not set -+# CONFIG_SND_ECHO3G is not set -+# CONFIG_SND_INDIGO is not set -+# CONFIG_SND_INDIGOIO is not set -+# CONFIG_SND_INDIGODJ is not set -+# CONFIG_SND_INDIGOIOX is not set -+# CONFIG_SND_INDIGODJX is not set -+# CONFIG_SND_EMU10K1 is not set -+# CONFIG_SND_EMU10K1X is not set -+# CONFIG_SND_ENS1370 is not set -+# CONFIG_SND_ENS1371 is not set -+# CONFIG_SND_ES1938 is not set -+# CONFIG_SND_ES1968 is not set -+# CONFIG_SND_FM801 is not set -+# CONFIG_SND_HDSP is not set -+# CONFIG_SND_HDSPM is not set -+# CONFIG_SND_ICE1712 is not set -+# CONFIG_SND_ICE1724 is not set -+# CONFIG_SND_INTEL8X0 is not set -+# CONFIG_SND_INTEL8X0M is not set -+# CONFIG_SND_KORG1212 is not set -+# CONFIG_SND_LOLA is not set -+# CONFIG_SND_LX6464ES is not set -+# CONFIG_SND_MAESTRO3 is not set -+# CONFIG_SND_MIXART is not set -+# CONFIG_SND_NM256 is not set -+# CONFIG_SND_PCXHR is not set -+# CONFIG_SND_RIPTIDE is not set -+# CONFIG_SND_RME32 is not set -+# CONFIG_SND_RME96 is not set -+# CONFIG_SND_RME9652 is not set -+# CONFIG_SND_SE6X is not set -+# CONFIG_SND_SONICVIBES is not set -+# CONFIG_SND_TRIDENT is not set -+# CONFIG_SND_VIA82XX is not set -+# CONFIG_SND_VIA82XX_MODEM is not set -+# CONFIG_SND_VIRTUOSO is not set -+# CONFIG_SND_VX222 is not set -+# CONFIG_SND_YMFPCI is not set -+ -+# -+# HD-Audio -+# -+CONFIG_SND_HDA=m -+# CONFIG_SND_HDA_INTEL is not set - CONFIG_SND_HDA_TEGRA=m -+# CONFIG_SND_HDA_HWDEP is not set -+# CONFIG_SND_HDA_RECONFIG is not set -+# CONFIG_SND_HDA_INPUT_BEEP is not set -+# CONFIG_SND_HDA_PATCH_LOADER is not set -+# CONFIG_SND_HDA_CODEC_REALTEK is not set -+# CONFIG_SND_HDA_CODEC_ANALOG is not set -+# CONFIG_SND_HDA_CODEC_SIGMATEL is not set -+# CONFIG_SND_HDA_CODEC_VIA is not set - CONFIG_SND_HDA_CODEC_HDMI=m -+# CONFIG_SND_HDA_CODEC_CIRRUS is not set -+# CONFIG_SND_HDA_CODEC_CONEXANT is not set -+# CONFIG_SND_HDA_CODEC_CA0110 is not set -+# CONFIG_SND_HDA_CODEC_CA0132 is not set -+# CONFIG_SND_HDA_CODEC_CMEDIA is not set -+# CONFIG_SND_HDA_CODEC_SI3054 is not set -+# CONFIG_SND_HDA_GENERIC is not set - CONFIG_SND_HDA_POWER_SAVE_DEFAULT=1 -+# end of HD-Audio -+ -+CONFIG_SND_HDA_CORE=m -+CONFIG_SND_HDA_ALIGNED_MMIO=y -+CONFIG_SND_HDA_PREALLOC_SIZE=64 -+CONFIG_SND_SPI=y -+CONFIG_SND_USB=y - CONFIG_SND_USB_AUDIO=y -+CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER=y -+# CONFIG_SND_USB_UA101 is not set -+# CONFIG_SND_USB_CAIAQ is not set -+# CONFIG_SND_USB_6FIRE is not set -+# CONFIG_SND_USB_HIFACE is not set -+# CONFIG_SND_BCD2000 is not set -+# CONFIG_SND_USB_POD is not set -+# CONFIG_SND_USB_PODHD is not set -+# CONFIG_SND_USB_TONEPORT is not set -+# CONFIG_SND_USB_VARIAX is not set - CONFIG_SND_SOC=y -+CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y -+CONFIG_SND_SOC_COMPRESS=y -+# CONFIG_SND_SOC_AMD_ACP is not set -+# CONFIG_SND_ATMEL_SOC is not set -+# CONFIG_SND_BCM63XX_I2S_WHISTLER is not set -+# CONFIG_SND_DESIGNWARE_I2S is not set -+ -+# -+# SoC Audio for Freescale CPUs -+# -+ -+# -+# Common SoC Audio options for Freescale CPUs: -+# -+# CONFIG_SND_SOC_FSL_ASRC is not set -+# CONFIG_SND_SOC_FSL_SAI is not set -+# CONFIG_SND_SOC_FSL_AUDMIX is not set -+# CONFIG_SND_SOC_FSL_SSI is not set -+# CONFIG_SND_SOC_FSL_SPDIF is not set -+# CONFIG_SND_SOC_FSL_ESAI is not set -+# CONFIG_SND_SOC_FSL_MICFIL is not set -+# CONFIG_SND_SOC_IMX_AUDMUX is not set -+# end of SoC Audio for Freescale CPUs -+ -+# CONFIG_SND_I2S_HI6210_I2S is not set -+# CONFIG_SND_SOC_IMG is not set -+# CONFIG_SND_SOC_MTK_BTCVSD is not set -+# CONFIG_SND_SOC_SOF_TOPLEVEL is not set -+ -+# -+# STMicroelectronics STM32 SOC audio support -+# -+# end of STMicroelectronics STM32 SOC audio support -+ - CONFIG_SND_SOC_TEGRA=m -+# CONFIG_SND_SOC_TEGRA20_AC97 is not set -+# CONFIG_SND_SOC_TEGRA20_DAS is not set -+# CONFIG_SND_SOC_TEGRA20_I2S is not set -+CONFIG_SND_SOC_TEGRA20_SPDIF=m -+# CONFIG_SND_SOC_TEGRA30_AHUB is not set -+# CONFIG_SND_SOC_TEGRA30_I2S is not set - CONFIG_SND_SOC_TEGRA210_AHUB=m - CONFIG_SND_SOC_TEGRA210_DMIC=m - CONFIG_SND_SOC_TEGRA210_I2S=m -@@ -985,20 +5567,199 @@ CONFIG_SND_SOC_TEGRA210_OPE=m - CONFIG_SND_SOC_TEGRA210_ADSP=m - CONFIG_SND_SOC_TEGRA210_AUDIO=m - CONFIG_SND_SOC_TEGRA_AUDIO_GRAPH_CARD=m -+# CONFIG_SND_SOC_TEGRA_RT5640 is not set -+# CONFIG_SND_SOC_TEGRA_WM8753 is not set -+# CONFIG_SND_SOC_TEGRA_WM8903 is not set -+# CONFIG_SND_SOC_TEGRA_WM9712 is not set -+# CONFIG_SND_SOC_TEGRA_TRIMSLICE is not set -+# CONFIG_SND_SOC_TEGRA_ALC5632 is not set -+# CONFIG_SND_SOC_TEGRA_MAX98090 is not set -+# CONFIG_SND_SOC_TEGRA_RT5677 is not set -+# CONFIG_SND_SOC_TEGRA_SGTL5000 is not set -+# CONFIG_SND_SOC_XILINX_I2S is not set -+# CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER is not set -+# CONFIG_SND_SOC_XILINX_SPDIF is not set -+# CONFIG_SND_SOC_XTFPGA_I2S is not set -+# CONFIG_ZX_TDM is not set -+CONFIG_SND_SOC_I2C_AND_SPI=y -+ -+# -+# CODEC drivers -+# -+# CONFIG_SND_SOC_AC97_CODEC is not set -+# CONFIG_SND_SOC_ADAU1701 is not set -+# CONFIG_SND_SOC_ADAU1761_I2C is not set -+# CONFIG_SND_SOC_ADAU1761_SPI is not set -+# CONFIG_SND_SOC_ADAU7002 is not set -+# CONFIG_SND_SOC_ADAU7118_HW is not set -+# CONFIG_SND_SOC_ADAU7118_I2C is not set -+# CONFIG_SND_SOC_AK4104 is not set -+# CONFIG_SND_SOC_AK4118 is not set -+# CONFIG_SND_SOC_AK4458 is not set -+# CONFIG_SND_SOC_AK4554 is not set - CONFIG_SND_SOC_AK4613=m -+# CONFIG_SND_SOC_AK4642 is not set -+# CONFIG_SND_SOC_AK5386 is not set -+# CONFIG_SND_SOC_AK5558 is not set -+# CONFIG_SND_SOC_ALC5623 is not set -+# CONFIG_SND_SOC_BD28623 is not set -+# CONFIG_SND_SOC_BT_SCO is not set -+# CONFIG_SND_SOC_CS35L32 is not set -+# CONFIG_SND_SOC_CS35L33 is not set -+# CONFIG_SND_SOC_CS35L34 is not set -+# CONFIG_SND_SOC_CS35L35 is not set -+# CONFIG_SND_SOC_CS35L36 is not set -+# CONFIG_SND_SOC_CS42L42 is not set -+# CONFIG_SND_SOC_CS42L51_I2C is not set -+# CONFIG_SND_SOC_CS42L52 is not set -+# CONFIG_SND_SOC_CS42L56 is not set -+# CONFIG_SND_SOC_CS42L73 is not set -+# CONFIG_SND_SOC_CS4234 is not set -+# CONFIG_SND_SOC_CS4265 is not set -+# CONFIG_SND_SOC_CS4270 is not set -+# CONFIG_SND_SOC_CS4271_I2C is not set -+# CONFIG_SND_SOC_CS4271_SPI is not set -+# CONFIG_SND_SOC_CS42XX8_I2C is not set -+# CONFIG_SND_SOC_CS43130 is not set -+# CONFIG_SND_SOC_CS4341 is not set -+# CONFIG_SND_SOC_CS4349 is not set -+# CONFIG_SND_SOC_CS53L30 is not set -+# CONFIG_SND_SOC_CX2072X is not set -+# CONFIG_SND_SOC_DA7213 is not set -+# CONFIG_SND_SOC_DMIC is not set -+CONFIG_SND_SOC_HDMI_CODEC=m - CONFIG_SND_SOC_ES7134=m -+# CONFIG_SND_SOC_ES7241 is not set -+# CONFIG_SND_SOC_ES8316 is not set -+# CONFIG_SND_SOC_ES8328_I2C is not set -+# CONFIG_SND_SOC_ES8328_SPI is not set -+# CONFIG_SND_SOC_GTM601 is not set -+# CONFIG_SND_SOC_INNO_RK3036 is not set -+# CONFIG_SND_SOC_MAX98088 is not set -+# CONFIG_SND_SOC_MAX98357A is not set -+# CONFIG_SND_SOC_MAX98504 is not set -+# CONFIG_SND_SOC_MAX9867 is not set - CONFIG_SND_SOC_MAX98927=m -+# CONFIG_SND_SOC_MAX98373_I2C is not set -+# CONFIG_SND_SOC_MAX98373_SDW is not set -+# CONFIG_SND_SOC_MAX98390 is not set -+# CONFIG_SND_SOC_MAX9860 is not set -+# CONFIG_SND_SOC_MSM8916_WCD_ANALOG is not set -+# CONFIG_SND_SOC_MSM8916_WCD_DIGITAL is not set -+# CONFIG_SND_SOC_PCM1681 is not set -+# CONFIG_SND_SOC_PCM1789_I2C is not set -+# CONFIG_SND_SOC_PCM179X_I2C is not set -+# CONFIG_SND_SOC_PCM179X_SPI is not set -+# CONFIG_SND_SOC_PCM186X_I2C is not set -+# CONFIG_SND_SOC_PCM186X_SPI is not set -+# CONFIG_SND_SOC_PCM3060_I2C is not set -+# CONFIG_SND_SOC_PCM3060_SPI is not set -+CONFIG_SND_SOC_PCM3168A=m - CONFIG_SND_SOC_PCM3168A_I2C=m -+# CONFIG_SND_SOC_PCM3168A_SPI is not set -+# CONFIG_SND_SOC_PCM512x_I2C is not set -+# CONFIG_SND_SOC_PCM512x_SPI is not set -+# CONFIG_SND_SOC_RK3328 is not set -+CONFIG_SND_SOC_RL6231=m -+# CONFIG_SND_SOC_RT1308_SDW is not set -+# CONFIG_SND_SOC_RT5616 is not set -+# CONFIG_SND_SOC_RT5631 is not set - CONFIG_SND_SOC_RT5640=m -+CONFIG_SND_SOC_RT5659=m -+# CONFIG_SND_SOC_RT5682_SDW is not set -+# CONFIG_SND_SOC_RT700_SDW is not set -+# CONFIG_SND_SOC_RT711_SDW is not set -+# CONFIG_SND_SOC_RT715_SDW is not set -+CONFIG_SND_SOC_SGTL5000=m -+# CONFIG_SND_SOC_SIMPLE_AMPLIFIER is not set -+# CONFIG_SND_SOC_SIRF_AUDIO_CODEC is not set - CONFIG_SND_SOC_SPDIF=m -+# CONFIG_SND_SOC_SSM2305 is not set -+# CONFIG_SND_SOC_SSM2602_SPI is not set -+# CONFIG_SND_SOC_SSM2602_I2C is not set -+# CONFIG_SND_SOC_SSM4567 is not set -+# CONFIG_SND_SOC_STA32X is not set -+# CONFIG_SND_SOC_STA350 is not set -+# CONFIG_SND_SOC_STI_SAS is not set -+CONFIG_SND_SOC_TAS2552=m -+# CONFIG_SND_SOC_TAS2562 is not set -+# CONFIG_SND_SOC_TAS2764 is not set -+# CONFIG_SND_SOC_TAS2770 is not set -+# CONFIG_SND_SOC_TAS5086 is not set - CONFIG_SND_SOC_TAS571X=m -+# CONFIG_SND_SOC_TAS5720 is not set -+# CONFIG_SND_SOC_TAS6424 is not set -+# CONFIG_SND_SOC_TDA7419 is not set -+# CONFIG_SND_SOC_TFA9879 is not set -+# CONFIG_SND_SOC_TLV320AIC23_I2C is not set -+# CONFIG_SND_SOC_TLV320AIC23_SPI is not set -+# CONFIG_SND_SOC_TLV320AIC31XX is not set -+# CONFIG_SND_SOC_TLV320AIC32X4_I2C is not set -+# CONFIG_SND_SOC_TLV320AIC32X4_SPI is not set -+# CONFIG_SND_SOC_TLV320AIC3X is not set -+# CONFIG_SND_SOC_TLV320ADCX140 is not set -+# CONFIG_SND_SOC_TS3A227E is not set -+# CONFIG_SND_SOC_TSCS42XX is not set -+# CONFIG_SND_SOC_TSCS454 is not set -+# CONFIG_SND_SOC_UDA1334 is not set -+# CONFIG_SND_SOC_WM8510 is not set -+# CONFIG_SND_SOC_WM8523 is not set -+# CONFIG_SND_SOC_WM8524 is not set -+# CONFIG_SND_SOC_WM8580 is not set -+# CONFIG_SND_SOC_WM8711 is not set -+# CONFIG_SND_SOC_WM8728 is not set -+# CONFIG_SND_SOC_WM8731 is not set -+# CONFIG_SND_SOC_WM8737 is not set -+# CONFIG_SND_SOC_WM8741 is not set -+# CONFIG_SND_SOC_WM8750 is not set -+# CONFIG_SND_SOC_WM8753 is not set -+# CONFIG_SND_SOC_WM8770 is not set -+# CONFIG_SND_SOC_WM8776 is not set -+# CONFIG_SND_SOC_WM8782 is not set -+# CONFIG_SND_SOC_WM8804_I2C is not set -+# CONFIG_SND_SOC_WM8804_SPI is not set -+# CONFIG_SND_SOC_WM8903 is not set -+# CONFIG_SND_SOC_WM8904 is not set -+# CONFIG_SND_SOC_WM8960 is not set -+# CONFIG_SND_SOC_WM8962 is not set -+# CONFIG_SND_SOC_WM8974 is not set -+# CONFIG_SND_SOC_WM8978 is not set -+# CONFIG_SND_SOC_WM8985 is not set -+# CONFIG_SND_SOC_WSA881X is not set -+# CONFIG_SND_SOC_ZL38060 is not set -+# CONFIG_SND_SOC_ZX_AUD96P22 is not set -+# CONFIG_SND_SOC_MAX9759 is not set -+# CONFIG_SND_SOC_MT6351 is not set -+# CONFIG_SND_SOC_MT6358 is not set -+# CONFIG_SND_SOC_MT6660 is not set -+# CONFIG_SND_SOC_NAU8540 is not set -+# CONFIG_SND_SOC_NAU8810 is not set -+# CONFIG_SND_SOC_NAU8822 is not set -+# CONFIG_SND_SOC_NAU8824 is not set -+# CONFIG_SND_SOC_TPA6130A2 is not set -+# end of CODEC drivers -+ -+CONFIG_SND_SIMPLE_CARD_UTILS=m - CONFIG_SND_SIMPLE_CARD=m - CONFIG_SND_AUDIO_GRAPH_CARD=m - CONFIG_SND_SOC_TEGRA210_ADSP_VIRT_ALT=m - CONFIG_SND_SOC_TEGRA_VIRT_T210REF_PCM=m -+CONFIG_SND_T23X_SAFETY_I2S=m -+ -+# -+# HID support -+# -+CONFIG_HID=y -+# CONFIG_HID_BATTERY_STRENGTH is not set - CONFIG_HIDRAW=y - CONFIG_UHID=y -+CONFIG_HID_GENERIC=y -+ -+# -+# Special HID drivers -+# - CONFIG_HID_A4TECH=m -+# CONFIG_HID_ACCUTOUCH is not set - CONFIG_HID_ACRUX=y - CONFIG_HID_ACRUX_FF=y - CONFIG_HID_APPLE=y -@@ -1007,41 +5768,62 @@ CONFIG_HID_ASUS=m - CONFIG_HID_AUREAL=m - CONFIG_HID_BELKIN=m - CONFIG_HID_BETOP_FF=m -+# CONFIG_HID_BIGBEN_FF is not set - CONFIG_HID_CHERRY=m - CONFIG_HID_CHICONY=m - CONFIG_HID_CORSAIR=m -+# CONFIG_HID_COUGAR is not set -+# CONFIG_HID_MACALLY is not set - CONFIG_HID_PRODIKEYS=m - CONFIG_HID_CMEDIA=m -+# CONFIG_HID_CP2112 is not set -+# CONFIG_HID_CREATIVE_SB0540 is not set - CONFIG_HID_CYPRESS=m - CONFIG_HID_DRAGONRISE=y - CONFIG_DRAGONRISE_FF=y - CONFIG_HID_EMS_FF=m -+# CONFIG_HID_ELAN is not set - CONFIG_HID_ELECOM=m - CONFIG_HID_ELO=m - CONFIG_HID_EZKEY=m - CONFIG_HID_GEMBIRD=m - CONFIG_HID_GFRM=m -+# CONFIG_HID_GLORIOUS is not set - CONFIG_HID_HOLTEK=y -+# CONFIG_HOLTEK_FF is not set -+# CONFIG_HID_VIVALDI is not set - CONFIG_HID_GT683R=m - CONFIG_HID_KEYTOUCH=y - CONFIG_HID_KYE=y - CONFIG_HID_UCLOGIC=y - CONFIG_HID_WALTOP=y -+# CONFIG_HID_VIEWSONIC is not set - CONFIG_HID_GYRATION=y - CONFIG_HID_ICADE=m - CONFIG_HID_ITE=m -+# CONFIG_HID_JABRA is not set - CONFIG_HID_TWINHAN=y - CONFIG_HID_KENSINGTON=m - CONFIG_HID_LCPOWER=y -+CONFIG_HID_LED=m - CONFIG_HID_LENOVO=m - CONFIG_HID_LOGITECH=m - CONFIG_HID_LOGITECH_DJ=m -+CONFIG_HID_LOGITECH_HIDPP=m -+# CONFIG_LOGITECH_FF is not set -+# CONFIG_LOGIRUMBLEPAD2_FF is not set -+# CONFIG_LOGIG940_FF is not set -+# CONFIG_LOGIWHEELS_FF is not set - CONFIG_HID_MAGICMOUSE=y -+# CONFIG_HID_MALTRON is not set -+# CONFIG_HID_MAYFLASH is not set -+# CONFIG_HID_REDRAGON is not set - CONFIG_HID_MICROSOFT=m - CONFIG_HID_MONTEREY=m - CONFIG_HID_MULTITOUCH=y - CONFIG_HID_NTI=m - CONFIG_HID_NTRIG=y -+# CONFIG_HID_NVIDIA_STAND is not set - CONFIG_HID_ORTEK=y - CONFIG_HID_PANTHERLORD=y - CONFIG_PANTHERLORD_FF=y -@@ -1050,6 +5832,9 @@ CONFIG_HID_PETALYNX=m - CONFIG_HID_PICOLCD=m - CONFIG_HID_PICOLCD_FB=y - CONFIG_HID_PICOLCD_BACKLIGHT=y -+# CONFIG_HID_PICOLCD_LCD is not set -+# CONFIG_HID_PICOLCD_LEDS is not set -+# CONFIG_HID_PICOLCD_CIR is not set - CONFIG_HID_PLANTRONICS=m - CONFIG_HID_PRIMAX=m - CONFIG_HID_RETRODE=m -@@ -1057,7 +5842,9 @@ CONFIG_HID_ROCCAT=m - CONFIG_HID_SAITEK=m - CONFIG_HID_SAMSUNG=m - CONFIG_HID_SONY=m -+# CONFIG_SONY_FF is not set - CONFIG_HID_SPEEDLINK=m -+# CONFIG_HID_STEAM is not set - CONFIG_HID_STEELSERIES=m - CONFIG_HID_SUNPLUS=m - CONFIG_HID_RMI=m -@@ -1069,28 +5856,112 @@ CONFIG_HID_TIVO=m - CONFIG_HID_TOPSEED=m - CONFIG_HID_THINGM=m - CONFIG_HID_THRUSTMASTER=m -+# CONFIG_THRUSTMASTER_FF is not set - CONFIG_HID_UDRAW_PS3=m -+# CONFIG_HID_U2FZERO is not set - CONFIG_HID_WACOM=m - CONFIG_HID_WIIMOTE=m - CONFIG_HID_XINMO=m - CONFIG_HID_ZEROPLUS=m -+# CONFIG_ZEROPLUS_FF is not set - CONFIG_HID_ZYDACRON=m - CONFIG_HID_SENSOR_HUB=m - CONFIG_HID_SENSOR_CUSTOM_SENSOR=m - CONFIG_HID_ALPS=m -+# CONFIG_HID_MCP2221 is not set -+# end of Special HID drivers -+ -+# -+# USB HID support -+# -+CONFIG_USB_HID=y -+# CONFIG_HID_PID is not set - CONFIG_USB_HIDDEV=y -+# end of USB HID support -+ -+# -+# I2C HID support -+# -+# CONFIG_I2C_HID is not set -+# end of I2C HID support -+ -+# -+# SHIELD accessory HID drivers -+# -+# CONFIG_HID_SHIELD_BLAKE is not set - CONFIG_HID_SHIELD_REMOTE=m -+# end of SHIELD accessory HID drivers -+# end of HID support -+ -+CONFIG_USB_OHCI_LITTLE_ENDIAN=y -+CONFIG_USB_SUPPORT=y -+CONFIG_USB_COMMON=y -+# CONFIG_USB_LED_TRIG is not set -+CONFIG_USB_ULPI_BUS=m -+CONFIG_USB_CONN_GPIO=y -+CONFIG_USB_ARCH_HAS_HCD=y -+CONFIG_USB=y -+CONFIG_USB_PCI=y - CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -+ -+# -+# Miscellaneous USB options -+# -+CONFIG_USB_DEFAULT_PERSIST=y -+# CONFIG_USB_FEW_INIT_RETRIES is not set -+# CONFIG_USB_DYNAMIC_MINORS is not set - CONFIG_USB_OTG=y -+# CONFIG_USB_OTG_PRODUCTLIST is not set -+# CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB is not set -+# CONFIG_USB_OTG_FSM is not set -+# CONFIG_USB_LEDS_TRIGGER_USBPORT is not set -+CONFIG_USB_AUTOSUSPEND_DELAY=2 - CONFIG_USB_MON=m -+ -+# -+# USB Host Controller Drivers -+# -+# CONFIG_USB_C67X00_HCD is not set - CONFIG_USB_XHCI_HCD=y -+# CONFIG_USB_XHCI_DBGCAP is not set -+CONFIG_USB_XHCI_PCI=y -+# CONFIG_USB_XHCI_PCI_RENESAS is not set -+# CONFIG_USB_XHCI_PLATFORM is not set - CONFIG_USB_XHCI_TEGRA=y -+# CONFIG_USB_EHCI_HCD is not set -+# CONFIG_USB_OXU210HP_HCD is not set -+# CONFIG_USB_ISP116X_HCD is not set -+# CONFIG_USB_FOTG210_HCD is not set -+# CONFIG_USB_MAX3421_HCD is not set - CONFIG_USB_OHCI_HCD=y -+CONFIG_USB_OHCI_HCD_PCI=y - CONFIG_USB_OHCI_HCD_PLATFORM=y -+# CONFIG_USB_UHCI_HCD is not set -+# CONFIG_USB_SL811_HCD is not set -+# CONFIG_USB_R8A66597_HCD is not set -+# CONFIG_USB_HCD_BCMA is not set -+# CONFIG_USB_HCD_SSB is not set -+# CONFIG_USB_HCD_TEST_MODE is not set -+ -+# -+# USB Device Class drivers -+# - CONFIG_USB_ACM=m - CONFIG_USB_PRINTER=m -+CONFIG_USB_WDM=m -+# CONFIG_USB_TMC is not set -+ -+# -+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may -+# -+ -+# -+# also be needed; see USB_STORAGE Help for more info -+# - CONFIG_USB_STORAGE=y -+# CONFIG_USB_STORAGE_DEBUG is not set - CONFIG_USB_STORAGE_REALTEK=m -+CONFIG_REALTEK_AUTOPM=y - CONFIG_USB_STORAGE_DATAFAB=m - CONFIG_USB_STORAGE_FREECOM=m - CONFIG_USB_STORAGE_ISD200=m -@@ -1104,100 +5975,723 @@ CONFIG_USB_STORAGE_KARMA=m - CONFIG_USB_STORAGE_CYPRESS_ATACB=m - CONFIG_USB_STORAGE_ENE_UB6250=m - CONFIG_USB_UAS=y -+ -+# -+# USB Imaging devices -+# - CONFIG_USB_MDC800=m -+# CONFIG_USB_MICROTEK is not set -+# CONFIG_USBIP_CORE is not set -+# CONFIG_USB_CDNS3 is not set -+# CONFIG_USB_MUSB_HDRC is not set -+# CONFIG_USB_DWC3 is not set -+# CONFIG_USB_DWC2 is not set - CONFIG_USB_CHIPIDEA=m -+# CONFIG_USB_CHIPIDEA_UDC is not set -+CONFIG_USB_CHIPIDEA_MSM=m -+CONFIG_USB_CHIPIDEA_IMX=m -+CONFIG_USB_CHIPIDEA_GENERIC=m -+# CONFIG_USB_ISP1760 is not set -+ -+# -+# USB port drivers -+# - CONFIG_USB_SERIAL=m -+# CONFIG_USB_SERIAL_GENERIC is not set -+# CONFIG_USB_SERIAL_SIMPLE is not set -+# CONFIG_USB_SERIAL_AIRCABLE is not set -+# CONFIG_USB_SERIAL_ARK3116 is not set -+# CONFIG_USB_SERIAL_BELKIN is not set - CONFIG_USB_SERIAL_CH341=m -+# CONFIG_USB_SERIAL_WHITEHEAT is not set -+# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set - CONFIG_USB_SERIAL_CP210X=m -+# CONFIG_USB_SERIAL_CYPRESS_M8 is not set -+# CONFIG_USB_SERIAL_EMPEG is not set - CONFIG_USB_SERIAL_FTDI_SIO=m -+# CONFIG_USB_SERIAL_VISOR is not set -+# CONFIG_USB_SERIAL_IPAQ is not set -+# CONFIG_USB_SERIAL_IR is not set -+# CONFIG_USB_SERIAL_EDGEPORT is not set -+# CONFIG_USB_SERIAL_EDGEPORT_TI is not set -+# CONFIG_USB_SERIAL_F81232 is not set -+# CONFIG_USB_SERIAL_F8153X is not set - CONFIG_USB_SERIAL_GARMIN=m -+# CONFIG_USB_SERIAL_IPW is not set -+# CONFIG_USB_SERIAL_IUU is not set -+# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set - CONFIG_USB_SERIAL_KEYSPAN=m -+# CONFIG_USB_SERIAL_KLSI is not set -+# CONFIG_USB_SERIAL_KOBIL_SCT is not set -+# CONFIG_USB_SERIAL_MCT_U232 is not set -+# CONFIG_USB_SERIAL_METRO is not set -+# CONFIG_USB_SERIAL_MOS7720 is not set -+# CONFIG_USB_SERIAL_MOS7840 is not set -+# CONFIG_USB_SERIAL_MXUPORT is not set -+# CONFIG_USB_SERIAL_NAVMAN is not set - CONFIG_USB_SERIAL_PL2303=m -+# CONFIG_USB_SERIAL_OTI6858 is not set -+# CONFIG_USB_SERIAL_QCAUX is not set -+# CONFIG_USB_SERIAL_QUALCOMM is not set -+# CONFIG_USB_SERIAL_SPCP8X5 is not set -+# CONFIG_USB_SERIAL_SAFE is not set -+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set -+# CONFIG_USB_SERIAL_SYMBOL is not set -+# CONFIG_USB_SERIAL_TI is not set -+# CONFIG_USB_SERIAL_CYBERJACK is not set -+# CONFIG_USB_SERIAL_XIRCOM is not set -+CONFIG_USB_SERIAL_WWAN=m - CONFIG_USB_SERIAL_OPTION=m -+# CONFIG_USB_SERIAL_OMNINET is not set -+# CONFIG_USB_SERIAL_OPTICON is not set - CONFIG_USB_SERIAL_XSENS_MT=m -+# CONFIG_USB_SERIAL_WISHBONE is not set -+# CONFIG_USB_SERIAL_SSU100 is not set -+# CONFIG_USB_SERIAL_QT2 is not set -+# CONFIG_USB_SERIAL_UPD78F0730 is not set -+# CONFIG_USB_SERIAL_DEBUG is not set -+ -+# -+# USB Miscellaneous drivers -+# - CONFIG_USB_EMI62=m - CONFIG_USB_EMI26=m -+# CONFIG_USB_ADUTUX is not set - CONFIG_USB_SEVSEG=m -+# CONFIG_USB_LEGOTOWER is not set - CONFIG_USB_LCD=m - CONFIG_USB_CYPRESS_CY7C63=m - CONFIG_USB_CYTHERM=m - CONFIG_USB_IDMOUSE=m -+# CONFIG_USB_FTDI_ELAN is not set - CONFIG_USB_APPLEDISPLAY=m -+# CONFIG_APPLE_MFI_FASTCHARGE is not set - CONFIG_USB_LD=m -+# CONFIG_USB_TRANCEVIBRATOR is not set -+# CONFIG_USB_IOWARRIOR is not set - CONFIG_USB_TEST=m - CONFIG_USB_EHSET_TEST_FIXTURE=m -+# CONFIG_USB_ISIGHTFW is not set - CONFIG_USB_YUREX=m -+CONFIG_USB_EZUSB_FX2=m -+# CONFIG_USB_HUB_USB251XB is not set -+# CONFIG_USB_HSIC_USB3503 is not set - CONFIG_USB_HSIC_USB4604=m -+# CONFIG_USB_LINK_LAYER_TEST is not set -+# CONFIG_USB_CHAOSKEY is not set -+ -+# -+# USB Physical Layer drivers -+# -+CONFIG_USB_PHY=y -+# CONFIG_NOP_USB_XCEIV is not set -+# CONFIG_USB_GPIO_VBUS is not set -+# CONFIG_USB_ISP1301 is not set -+CONFIG_USB_TEGRA_PHY=m -+CONFIG_USB_ULPI=y -+CONFIG_USB_ULPI_VIEWPORT=y -+# end of USB Physical Layer drivers -+ - CONFIG_USB_GADGET=y -+# CONFIG_USB_GADGET_DEBUG is not set -+# CONFIG_USB_GADGET_DEBUG_FILES is not set -+# CONFIG_USB_GADGET_DEBUG_FS is not set -+CONFIG_USB_GADGET_VBUS_DRAW=2 -+CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 -+# CONFIG_U_SERIAL_CONSOLE is not set -+ -+# -+# USB Peripheral Controller -+# -+# CONFIG_USB_FOTG210_UDC is not set -+# CONFIG_USB_GR_UDC is not set -+# CONFIG_USB_R8A66597 is not set -+# CONFIG_USB_PXA27X is not set -+# CONFIG_USB_MV_UDC is not set -+# CONFIG_USB_MV_U3D is not set -+# CONFIG_USB_SNP_UDC_PLAT is not set -+# CONFIG_USB_M66592 is not set -+# CONFIG_USB_BDC_UDC is not set -+# CONFIG_USB_AMD5536UDC is not set -+# CONFIG_USB_NET2272 is not set -+# CONFIG_USB_NET2280 is not set -+# CONFIG_USB_GOKU is not set -+# CONFIG_USB_EG20T is not set -+# CONFIG_USB_GADGET_XILINX is not set -+# CONFIG_USB_MAX3420_UDC is not set - CONFIG_USB_TEGRA_XUDC=y -+# CONFIG_USB_DUMMY_HCD is not set -+# end of USB Peripheral Controller -+ -+CONFIG_USB_LIBCOMPOSITE=y -+CONFIG_USB_F_ACM=y -+CONFIG_USB_F_SS_LB=y -+CONFIG_USB_U_SERIAL=y -+CONFIG_USB_U_ETHER=y -+CONFIG_USB_F_NCM=y -+CONFIG_USB_F_ECM=y -+CONFIG_USB_F_RNDIS=y -+CONFIG_USB_F_MASS_STORAGE=y -+CONFIG_USB_F_FS=y -+CONFIG_USB_F_ACC=y - CONFIG_USB_CONFIGFS=y -+# CONFIG_USB_CONFIGFS_SERIAL is not set - CONFIG_USB_CONFIGFS_ACM=y -+# CONFIG_USB_CONFIGFS_OBEX is not set - CONFIG_USB_CONFIGFS_NCM=y - CONFIG_USB_CONFIGFS_ECM=y -+# CONFIG_USB_CONFIGFS_ECM_SUBSET is not set - CONFIG_USB_CONFIGFS_RNDIS=y -+# CONFIG_USB_CONFIGFS_EEM is not set - CONFIG_USB_CONFIGFS_MASS_STORAGE=y - CONFIG_USB_CONFIGFS_F_LB_SS=y - CONFIG_USB_CONFIGFS_F_FS=y - CONFIG_USB_CONFIGFS_F_ACC=y -+# CONFIG_USB_CONFIGFS_F_UAC1 is not set -+# CONFIG_USB_CONFIGFS_F_UAC1_LEGACY is not set -+# CONFIG_USB_CONFIGFS_F_UAC2 is not set -+# CONFIG_USB_CONFIGFS_F_MIDI is not set -+# CONFIG_USB_CONFIGFS_F_HID is not set -+# CONFIG_USB_CONFIGFS_F_UVC is not set -+# CONFIG_USB_CONFIGFS_F_PRINTER is not set -+ -+# -+# USB Gadget precomposed configurations -+# -+# CONFIG_USB_ZERO is not set -+# CONFIG_USB_AUDIO is not set -+# CONFIG_USB_ETH is not set -+# CONFIG_USB_G_NCM is not set -+# CONFIG_USB_GADGETFS is not set -+# CONFIG_USB_FUNCTIONFS is not set -+# CONFIG_USB_MASS_STORAGE is not set -+# CONFIG_USB_G_SERIAL is not set -+# CONFIG_USB_MIDI_GADGET is not set -+# CONFIG_USB_G_PRINTER is not set -+# CONFIG_USB_CDC_COMPOSITE is not set -+# CONFIG_USB_G_ACM_MS is not set -+# CONFIG_USB_G_MULTI is not set -+# CONFIG_USB_G_HID is not set -+# CONFIG_USB_G_DBGP is not set -+# CONFIG_USB_G_WEBCAM is not set -+# CONFIG_USB_RAW_GADGET is not set -+# end of USB Gadget precomposed configurations -+ - CONFIG_TYPEC=m --CONFIG_TYPEC_FUSB301=m -+# CONFIG_TYPEC_TCPM is not set - CONFIG_TYPEC_UCSI=m - CONFIG_UCSI_CCG=m -+# CONFIG_UCSI_ACPI is not set -+# CONFIG_TYPEC_HD3SS3220 is not set -+# CONFIG_TYPEC_TPS6598X is not set - CONFIG_TYPEC_STUSB160X=m -+CONFIG_TYPEC_FUSB301=m -+ -+# -+# USB Type-C Multiplexer/DeMultiplexer Switch support -+# -+# CONFIG_TYPEC_MUX_PI3USB30532 is not set -+# end of USB Type-C Multiplexer/DeMultiplexer Switch support -+ -+# -+# USB Type-C Alternate Mode drivers -+# -+# CONFIG_TYPEC_DP_ALTMODE is not set -+# end of USB Type-C Alternate Mode drivers -+ -+CONFIG_USB_ROLE_SWITCH=y - CONFIG_MMC=y -+CONFIG_PWRSEQ_EMMC=y -+# CONFIG_PWRSEQ_SD8787 is not set -+CONFIG_PWRSEQ_SIMPLE=y -+CONFIG_MMC_BLOCK=y - CONFIG_MMC_BLOCK_MINORS=32 -+# CONFIG_SDIO_UART is not set - CONFIG_MMC_TEST=m -+# CONFIG_MMC_FFU is not set -+ -+# -+# MMC/SD/SDIO Host Controller Drivers -+# -+# CONFIG_MMC_DEBUG is not set - CONFIG_MMC_ARMMMCI=y - # CONFIG_MMC_STM32_SDMMC is not set - CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_IO_ACCESSORS=y -+# CONFIG_MMC_SDHCI_PCI is not set -+# CONFIG_MMC_SDHCI_ACPI is not set - CONFIG_MMC_SDHCI_PLTFM=y -+# CONFIG_MMC_SDHCI_OF_ARASAN is not set -+# CONFIG_MMC_SDHCI_OF_ASPEED is not set -+# CONFIG_MMC_SDHCI_OF_AT91 is not set -+# CONFIG_MMC_SDHCI_OF_DWCMSHC is not set -+# CONFIG_MMC_SDHCI_CADENCE is not set - CONFIG_MMC_SDHCI_TEGRA=y -+# CONFIG_MMC_SDHCI_F_SDH30 is not set -+# CONFIG_MMC_SDHCI_MILBEAUT is not set -+# CONFIG_MMC_TIFM_SD is not set - CONFIG_MMC_SPI=m -+# CONFIG_MMC_CB710 is not set -+# CONFIG_MMC_VIA_SDMMC is not set - CONFIG_MMC_DW=y -+CONFIG_MMC_DW_PLTFM=y -+# CONFIG_MMC_DW_BLUEFIELD is not set -+# CONFIG_MMC_DW_EXYNOS is not set -+# CONFIG_MMC_DW_HI3798CV200 is not set -+# CONFIG_MMC_DW_K3 is not set -+# CONFIG_MMC_DW_PCI is not set -+# CONFIG_MMC_VUB300 is not set -+# CONFIG_MMC_USHC is not set -+# CONFIG_MMC_USDHI6ROL0 is not set -+CONFIG_MMC_CQHCI=y -+# CONFIG_MMC_HSQ is not set -+# CONFIG_MMC_TOSHIBA_PCI is not set -+# CONFIG_MMC_MTK is not set - CONFIG_MMC_SDHCI_XENON=y -+# CONFIG_MMC_SDHCI_OMAP is not set -+# CONFIG_MMC_SDHCI_AM654 is not set -+# CONFIG_MEMSTICK is not set -+CONFIG_NEW_LEDS=y - CONFIG_LEDS_CLASS=y -+# CONFIG_LEDS_CLASS_FLASH is not set -+# CONFIG_LEDS_CLASS_MULTICOLOR is not set -+# CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set -+ -+# -+# LED drivers -+# -+# CONFIG_LEDS_AN30259A is not set -+# CONFIG_LEDS_AW2013 is not set -+# CONFIG_LEDS_BCM6328 is not set -+# CONFIG_LEDS_BCM6358 is not set -+# CONFIG_LEDS_CR0014114 is not set -+# CONFIG_LEDS_EL15203000 is not set -+# CONFIG_LEDS_LM3530 is not set -+# CONFIG_LEDS_LM3532 is not set -+# CONFIG_LEDS_LM3642 is not set -+# CONFIG_LEDS_LM3692X is not set -+# CONFIG_LEDS_PCA9532 is not set - CONFIG_LEDS_GPIO=m -+# CONFIG_LEDS_LP3944 is not set -+# CONFIG_LEDS_LP3952 is not set -+# CONFIG_LEDS_LP50XX is not set -+# CONFIG_LEDS_LP55XX_COMMON is not set -+# CONFIG_LEDS_LP8860 is not set -+# CONFIG_LEDS_PCA955X is not set -+# CONFIG_LEDS_PCA963X is not set -+# CONFIG_LEDS_DAC124S085 is not set - CONFIG_LEDS_PWM=m -+# CONFIG_LEDS_REGULATOR is not set - CONFIG_LEDS_BD2802=m -+# CONFIG_LEDS_LT3593 is not set -+# CONFIG_LEDS_TCA6507 is not set -+# CONFIG_LEDS_TLC591XX is not set -+# CONFIG_LEDS_LM355x is not set - CONFIG_LEDS_IS31FL319X=m -+# CONFIG_LEDS_IS31FL32XX is not set -+ -+# -+# LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM) -+# -+# CONFIG_LEDS_BLINKM is not set -+# CONFIG_LEDS_SYSCON is not set -+# CONFIG_LEDS_MLXREG is not set -+# CONFIG_LEDS_USER is not set -+# CONFIG_LEDS_SPI_BYTE is not set -+# CONFIG_LEDS_TI_LMU_COMMON is not set -+ -+# -+# LED Triggers -+# -+CONFIG_LEDS_TRIGGERS=y -+# CONFIG_LEDS_TRIGGER_TIMER is not set -+# CONFIG_LEDS_TRIGGER_ONESHOT is not set -+# CONFIG_LEDS_TRIGGER_DISK is not set -+# CONFIG_LEDS_TRIGGER_MTD is not set -+# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set -+# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set -+# CONFIG_LEDS_TRIGGER_CPU is not set -+# CONFIG_LEDS_TRIGGER_ACTIVITY is not set -+# CONFIG_LEDS_TRIGGER_GPIO is not set -+# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set -+ -+# -+# iptables trigger is under Netfilter config (LED target) -+# -+# CONFIG_LEDS_TRIGGER_TRANSIENT is not set -+# CONFIG_LEDS_TRIGGER_CAMERA is not set -+# CONFIG_LEDS_TRIGGER_PANIC is not set -+# CONFIG_LEDS_TRIGGER_NETDEV is not set -+# CONFIG_LEDS_TRIGGER_PATTERN is not set -+# CONFIG_LEDS_TRIGGER_AUDIO is not set -+# CONFIG_LEDS_CY8C is not set -+# CONFIG_ACCESSIBILITY is not set - CONFIG_INFINIBAND=m - CONFIG_INFINIBAND_USER_MAD=m - CONFIG_INFINIBAND_USER_ACCESS=m -+CONFIG_INFINIBAND_USER_MEM=y -+CONFIG_INFINIBAND_ON_DEMAND_PAGING=y -+CONFIG_INFINIBAND_ADDR_TRANS=y -+CONFIG_INFINIBAND_ADDR_TRANS_CONFIGFS=y -+CONFIG_INFINIBAND_VIRT_DMA=y - CONFIG_INFINIBAND_MTHCA=m -+CONFIG_INFINIBAND_MTHCA_DEBUG=y -+# CONFIG_INFINIBAND_CXGB4 is not set -+# CONFIG_INFINIBAND_EFA is not set -+# CONFIG_INFINIBAND_I40IW is not set - CONFIG_MLX4_INFINIBAND=m - CONFIG_MLX5_INFINIBAND=m -+# CONFIG_INFINIBAND_OCRDMA is not set -+# CONFIG_INFINIBAND_HNS is not set -+# CONFIG_RDMA_RXE is not set -+# CONFIG_RDMA_SIW is not set - CONFIG_INFINIBAND_IPOIB=m - CONFIG_INFINIBAND_IPOIB_CM=y -+CONFIG_INFINIBAND_IPOIB_DEBUG=y -+# CONFIG_INFINIBAND_IPOIB_DEBUG_DATA is not set - CONFIG_INFINIBAND_SRP=m -+# CONFIG_INFINIBAND_ISER is not set -+# CONFIG_INFINIBAND_RTRS_CLIENT is not set -+# CONFIG_INFINIBAND_RTRS_SERVER is not set -+CONFIG_EDAC_SUPPORT=y -+# CONFIG_EDAC is not set -+CONFIG_RTC_LIB=y - CONFIG_RTC_CLASS=y -+CONFIG_RTC_HCTOSYS=y - CONFIG_RTC_HCTOSYS_DEVICE="rtc1" -+CONFIG_RTC_SYSTOHC=y -+CONFIG_RTC_SYSTOHC_DEVICE="rtc1" -+# CONFIG_RTC_DEBUG is not set - # CONFIG_RTC_NVMEM is not set -+ -+# -+# RTC interfaces -+# -+CONFIG_RTC_INTF_SYSFS=y -+CONFIG_RTC_INTF_PROC=y -+CONFIG_RTC_INTF_DEV=y -+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set -+# CONFIG_RTC_DRV_TEST is not set -+ -+# -+# I2C RTC drivers -+# -+# CONFIG_RTC_DRV_ABB5ZES3 is not set -+# CONFIG_RTC_DRV_ABEOZ9 is not set -+# CONFIG_RTC_DRV_ABX80X is not set -+# CONFIG_RTC_DRV_DS1307 is not set -+# CONFIG_RTC_DRV_DS1374 is not set -+# CONFIG_RTC_DRV_DS1672 is not set -+# CONFIG_RTC_DRV_HYM8563 is not set -+# CONFIG_RTC_DRV_MAX6900 is not set - CONFIG_RTC_DRV_MAX77686=y -+# CONFIG_RTC_DRV_RK808 is not set -+# CONFIG_RTC_DRV_RS5C372 is not set -+# CONFIG_RTC_DRV_ISL1208 is not set -+# CONFIG_RTC_DRV_ISL12022 is not set -+# CONFIG_RTC_DRV_ISL12026 is not set -+# CONFIG_RTC_DRV_X1205 is not set -+# CONFIG_RTC_DRV_PCF8523 is not set -+# CONFIG_RTC_DRV_PCF85063 is not set -+# CONFIG_RTC_DRV_PCF85363 is not set -+# CONFIG_RTC_DRV_PCF8563 is not set -+# CONFIG_RTC_DRV_PCF8583 is not set -+# CONFIG_RTC_DRV_M41T80 is not set -+# CONFIG_RTC_DRV_BQ32K is not set -+# CONFIG_RTC_DRV_S35390A is not set -+# CONFIG_RTC_DRV_FM3130 is not set -+# CONFIG_RTC_DRV_RX8010 is not set -+# CONFIG_RTC_DRV_RX8581 is not set - CONFIG_RTC_DRV_RX8025=m -+# CONFIG_RTC_DRV_EM3027 is not set -+# CONFIG_RTC_DRV_RV3028 is not set -+# CONFIG_RTC_DRV_RV3032 is not set -+# CONFIG_RTC_DRV_RV8803 is not set -+# CONFIG_RTC_DRV_S5M is not set -+# CONFIG_RTC_DRV_SD3078 is not set -+ -+# -+# SPI RTC drivers -+# -+# CONFIG_RTC_DRV_M41T93 is not set -+# CONFIG_RTC_DRV_M41T94 is not set -+# CONFIG_RTC_DRV_DS1302 is not set -+# CONFIG_RTC_DRV_DS1305 is not set -+# CONFIG_RTC_DRV_DS1343 is not set -+# CONFIG_RTC_DRV_DS1347 is not set -+# CONFIG_RTC_DRV_DS1390 is not set -+# CONFIG_RTC_DRV_MAX6916 is not set -+# CONFIG_RTC_DRV_R9701 is not set -+# CONFIG_RTC_DRV_RX4581 is not set -+# CONFIG_RTC_DRV_RX6110 is not set -+# CONFIG_RTC_DRV_RS5C348 is not set -+# CONFIG_RTC_DRV_MAX6902 is not set -+# CONFIG_RTC_DRV_PCF2123 is not set -+# CONFIG_RTC_DRV_MCP795 is not set -+CONFIG_RTC_I2C_AND_SPI=y -+ -+# -+# SPI and I2C RTC drivers -+# -+# CONFIG_RTC_DRV_DS3232 is not set -+# CONFIG_RTC_DRV_PCF2127 is not set -+# CONFIG_RTC_DRV_RV3029C2 is not set -+ -+# -+# Platform RTC drivers -+# -+# CONFIG_RTC_DRV_DS1286 is not set -+# CONFIG_RTC_DRV_DS1511 is not set -+# CONFIG_RTC_DRV_DS1553 is not set -+# CONFIG_RTC_DRV_DS1685_FAMILY is not set -+# CONFIG_RTC_DRV_DS1742 is not set -+# CONFIG_RTC_DRV_DS2404 is not set -+# CONFIG_RTC_DRV_EFI is not set -+# CONFIG_RTC_DRV_STK17TA8 is not set -+# CONFIG_RTC_DRV_M48T86 is not set -+# CONFIG_RTC_DRV_M48T35 is not set -+# CONFIG_RTC_DRV_M48T59 is not set -+# CONFIG_RTC_DRV_MSM6242 is not set -+# CONFIG_RTC_DRV_BQ4802 is not set -+# CONFIG_RTC_DRV_RP5C01 is not set -+# CONFIG_RTC_DRV_V3020 is not set -+# CONFIG_RTC_DRV_ZYNQMP is not set -+ -+# -+# on-CPU RTC drivers -+# -+# CONFIG_RTC_DRV_PL030 is not set -+# CONFIG_RTC_DRV_PL031 is not set -+# CONFIG_RTC_DRV_CADENCE is not set -+# CONFIG_RTC_DRV_FTRTC010 is not set -+CONFIG_RTC_DRV_TEGRA=y -+# CONFIG_RTC_DRV_R7301 is not set -+ -+# -+# HID Sensor RTC drivers -+# -+# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set -+CONFIG_NVVRS_PSEQ_RTC=y - CONFIG_DMADEVICES=y -+# CONFIG_DMADEVICES_DEBUG is not set -+ -+# -+# DMA Devices -+# -+CONFIG_DMA_ENGINE=y -+CONFIG_DMA_VIRTUAL_CHANNELS=y -+CONFIG_DMA_ACPI=y -+CONFIG_DMA_OF=y -+# CONFIG_ALTERA_MSGDMA is not set -+# CONFIG_AMBA_PL08X is not set -+# CONFIG_BCM_SBA_RAID is not set -+# CONFIG_DW_AXI_DMAC is not set -+# CONFIG_FSL_EDMA is not set -+# CONFIG_FSL_QDMA is not set -+# CONFIG_HISI_DMA is not set -+# CONFIG_INTEL_IDMA64 is not set -+# CONFIG_MV_XOR_V2 is not set -+# CONFIG_PL330_DMA is not set -+# CONFIG_PLX_DMA is not set - CONFIG_TEGRA20_APB_DMA=y - CONFIG_TEGRA210_ADMA=m - CONFIG_TEGRA_GPC_DMA=y -+# CONFIG_XILINX_DMA is not set -+# CONFIG_XILINX_ZYNQMP_DMA is not set -+# CONFIG_XILINX_ZYNQMP_DPDMA is not set -+# CONFIG_QCOM_HIDMA_MGMT is not set -+# CONFIG_QCOM_HIDMA is not set -+# CONFIG_DW_DMAC is not set -+# CONFIG_DW_DMAC_PCI is not set -+# CONFIG_DW_EDMA is not set -+# CONFIG_DW_EDMA_PCIE is not set -+# CONFIG_SF_PDMA is not set -+ -+# -+# DMA Clients -+# -+# CONFIG_ASYNC_TX_DMA is not set - CONFIG_DMATEST=y -+CONFIG_DMA_ENGINE_RAID=y -+ -+# -+# DMABUF options -+# -+CONFIG_SYNC_FILE=y -+# CONFIG_SW_SYNC is not set -+# CONFIG_UDMABUF is not set -+# CONFIG_DMABUF_MOVE_NOTIFY is not set -+# CONFIG_DMABUF_SELFTESTS is not set -+CONFIG_DMABUF_DEFERRED_UNMAPPING=y - CONFIG_DMABUF_HEAPS=y - CONFIG_DMABUF_HEAPS_SYSTEM=y - CONFIG_DMABUF_HEAPS_CMA=y --CONFIG_VFIO=m --CONFIG_VFIO_PCI=m -+# end of DMABUF options -+ -+# CONFIG_AUXDISPLAY is not set -+CONFIG_UIO=m -+# CONFIG_UIO_CIF is not set -+# CONFIG_UIO_PDRV_GENIRQ is not set -+# CONFIG_UIO_DMEM_GENIRQ is not set -+# CONFIG_UIO_AEC is not set -+# CONFIG_UIO_SERCOS3 is not set -+# CONFIG_UIO_PCI_GENERIC is not set -+# CONFIG_UIO_NETX is not set -+# CONFIG_UIO_PRUSS is not set -+# CONFIG_UIO_MF624 is not set -+CONFIG_VFIO_IOMMU_TYPE1=y -+CONFIG_VFIO_VIRQFD=y -+CONFIG_VFIO=y -+# CONFIG_VFIO_NOIOMMU is not set -+CONFIG_VFIO_PCI=y -+CONFIG_VFIO_PCI_MMAP=y -+CONFIG_VFIO_PCI_INTX=y -+CONFIG_VFIO_PLATFORM=y -+# CONFIG_VFIO_AMBA is not set -+# CONFIG_VFIO_PLATFORM_CALXEDAXGMAC_RESET is not set -+# CONFIG_VFIO_PLATFORM_AMDXGBE_RESET is not set -+# CONFIG_VFIO_MDEV is not set - CONFIG_VIRT_DRIVERS=y --CONFIG_VIRTIO_PCI=m -+CONFIG_VIRTIO=y -+CONFIG_VIRTIO_MENU=y -+CONFIG_VIRTIO_PCI=y -+CONFIG_VIRTIO_PCI_LEGACY=y - CONFIG_VIRTIO_BALLOON=m --CONFIG_VIRTIO_MMIO=m -+# CONFIG_VIRTIO_INPUT is not set -+CONFIG_VIRTIO_MMIO=y -+# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set -+# CONFIG_VDPA is not set -+CONFIG_VHOST_MENU=y -+# CONFIG_VHOST_NET is not set -+# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set -+ -+# -+# Microsoft Hyper-V guest support -+# -+# end of Microsoft Hyper-V guest support -+ -+# CONFIG_GREYBUS is not set -+CONFIG_STAGING=y - CONFIG_PRISM2_USB=m -+# CONFIG_COMEDI is not set - CONFIG_RTL8192U=m - CONFIG_RTLLIB=m -+CONFIG_RTLLIB_CRYPTO_CCMP=m -+CONFIG_RTLLIB_CRYPTO_TKIP=m -+CONFIG_RTLLIB_CRYPTO_WEP=m - CONFIG_RTL8192E=m -+# CONFIG_RTL8723BS is not set - CONFIG_R8712U=m - CONFIG_R8188EU=m -+CONFIG_88EU_AP_MODE=y -+# CONFIG_RTS5208 is not set -+# CONFIG_VT6655 is not set -+# CONFIG_VT6656 is not set -+ -+# -+# IIO staging drivers -+# -+ -+# -+# Accelerometers -+# -+# CONFIG_ADIS16203 is not set -+# CONFIG_ADIS16240 is not set -+# end of Accelerometers -+ -+# -+# Analog to digital converters -+# -+# CONFIG_AD7816 is not set -+# CONFIG_AD7280 is not set -+# end of Analog to digital converters -+ -+# -+# Analog digital bi-direction converters -+# -+# CONFIG_ADT7316 is not set -+# end of Analog digital bi-direction converters -+ -+# -+# Capacitance to digital converters -+# -+# CONFIG_AD7150 is not set -+# CONFIG_AD7746 is not set -+# end of Capacitance to digital converters -+ -+# -+# Direct Digital Synthesis -+# -+# CONFIG_AD9832 is not set -+# CONFIG_AD9834 is not set -+# end of Direct Digital Synthesis -+ -+# -+# Network Analyzer, Impedance Converters -+# -+# CONFIG_AD5933 is not set -+# end of Network Analyzer, Impedance Converters -+ -+# -+# Active energy metering IC -+# -+# CONFIG_ADE7854 is not set -+# CONFIG_INA219 is not set -+# CONFIG_INA230 is not set -+# CONFIG_INA3221 is not set -+# end of Active energy metering IC -+ -+# -+# Resolver to digital converters -+# -+# CONFIG_AD2S1210 is not set -+# end of Resolver to digital converters -+# end of IIO staging drivers -+ -+# CONFIG_FB_SM750 is not set -+# CONFIG_MFD_NVEC is not set -+# CONFIG_STAGING_MEDIA is not set -+ -+# -+# Android -+# -+# end of Android -+ -+# CONFIG_STAGING_BOARD is not set -+# CONFIG_LTE_GDM724X is not set -+# CONFIG_GS_FPGABOOT is not set -+# CONFIG_UNISYSSPAR is not set -+# CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set -+# CONFIG_FB_TFT is not set -+# CONFIG_KS7010 is not set -+# CONFIG_PI433 is not set -+ -+# -+# Gasket devices -+# -+# CONFIG_STAGING_GASKET_FRAMEWORK is not set -+# end of Gasket devices -+ -+# CONFIG_XIL_AXIS_FIFO is not set -+# CONFIG_FIELDBUS_DEV is not set -+# CONFIG_KPC2000 is not set - CONFIG_QLGE=m -+# CONFIG_WFX is not set -+# CONFIG_SPMI_HISI3670 is not set -+# CONFIG_MFD_HI6421_SPMI is not set -+# CONFIG_USB_WPAN_HCD is not set - CONFIG_TEGRA_HTS_GTE=y -+# CONFIG_TEGRA_GTE_TEST is not set -+# CONFIG_DUMMY_MEMORY_CARVEOUT is not set -+# CONFIG_GOLDFISH is not set -+# CONFIG_CHROME_PLATFORMS is not set -+# CONFIG_MELLANOX_PLATFORM is not set - CONFIG_DENVER_CPU=y -+# CONFIG_DENVER_MCA is not set - CONFIG_TEGRA_AON=y -+# CONFIG_TEGRA_ARI_MCA is not set -+# CONFIG_TEGRA_BRIDGE_MCA is not set -+# CONFIG_TEGRA_A57_SERR is not set - CONFIG_TEGRA_BWMGR=y - CONFIG_TEGRA_CAMERA_RTCPU=y - CONFIG_TEGRA_CAMERA_HSP_MBOX_CLIENT=y -@@ -1205,147 +6699,1444 @@ CONFIG_TEGRA_FSICOM=y - CONFIG_TEGRA_EPL=y - CONFIG_TEGRA_DCE=y - CONFIG_TEGRA_ISOMGR=y -+CONFIG_TEGRA_ISOMGR_POOL_KB_PER_SEC=0 - CONFIG_TEGRA_ISOMGR_SYSFS=y -+# CONFIG_TEGRA_ISOMGR_MAX_ISO_BW_QUIRK is not set -+CONFIG_NV_TEGRA_MC=y -+# CONFIG_TEGRA_VPR is not set -+CONFIG_TEGRA_MCE=y -+CONFIG_TEGRA_CACHE=y -+CONFIG_TEGRA_OF_MCERR=y -+# CONFIG_TEGRA_PM_IRQ is not set -+# CONFIG_TEGRA_PMC_AO_WAKE is not set -+# CONFIG_TEGRA_WAKEUP is not set -+CONFIG_TEGRA_PTP_NOTIFIER=y -+CONFIG_TEGRA_SOC_HWPM=y - CONFIG_TEGRA_SPE=y - CONFIG_TEGRA_SPE_HSP_MBOX_CLIENT=y -+# CONFIG_TEGRA_CBB_NOC is not set -+# CONFIG_TEGRA_HV_XHCI_DEBUG is not set -+# CONFIG_TEGRA_NVDUMPER is not set - CONFIG_TEGRA_CENTRAL_ACTMON=y -+CONFIG_TEGRA_FIRMWARES_CLASS=y -+CONFIG_TEGRA_FIRMWARES_INVENTORY=y -+# CONFIG_TEGRA_FIQ_DEBUGGER is not set - CONFIG_TEGRA_BOOTLOADER_DEBUG=m -+CONFIG_TEGRA_BOOTLOADER_DEBUG_INIT=y -+# CONFIG_TEGRA_BOOTLOADER_BOOT_CFG is not set -+CONFIG_NV_TEGRA_IVC=y -+# CONFIG_TEGRA_PM_DEBUG is not set - CONFIG_TEGRA_CLOCKS_CONFIGURE=y - CONFIG_TEGRA_USS_IO_PROXY=y -+CONFIG_TEGRA_CVNAS=y - CONFIG_TEGRA_SAFETY=y -+# CONFIG_TEGRA_SAFETY_IVC_DEBUG is not set -+# CONFIG_TEGRA_HSIERRRPTINJ is not set -+CONFIG_TEGRA_T234_HWPM=y - CONFIG_TEGRA_NVADSP=m - CONFIG_TEGRA_NVADSP_ON_SMMU=y -+# CONFIG_TEGRA_ADSP_DFS is not set -+# CONFIG_TEGRA_ADSP_CPUSTAT is not set - CONFIG_TEGRA_ADSP_FILEIO=y - CONFIG_TEGRA_ADSP_LPTHREAD=y -+# CONFIG_TEGRA_EMC_APE_DFS is not set -+CONFIG_TEGRA_ADSP_CONSOLE=y -+# CONFIG_MBOX_ACK_HANDLER is not set - CONFIG_TEGRA_VIRT_AUDIO_IVC=y -+CONFIG_HAVE_CLK=y -+CONFIG_CLKDEV_LOOKUP=y -+CONFIG_HAVE_CLK_PREPARE=y -+CONFIG_COMMON_CLK=y -+# CONFIG_COMMON_CLK_MAX77686 is not set -+# CONFIG_COMMON_CLK_MAX9485 is not set -+# CONFIG_COMMON_CLK_RK808 is not set -+# CONFIG_COMMON_CLK_SCPI is not set -+# CONFIG_COMMON_CLK_SI5341 is not set -+# CONFIG_COMMON_CLK_SI5351 is not set -+# CONFIG_COMMON_CLK_SI514 is not set -+# CONFIG_COMMON_CLK_SI544 is not set -+# CONFIG_COMMON_CLK_SI570 is not set -+# CONFIG_COMMON_CLK_CDCE706 is not set -+# CONFIG_COMMON_CLK_CDCE925 is not set -+# CONFIG_COMMON_CLK_CS2000_CP is not set -+# CONFIG_COMMON_CLK_S2MPS11 is not set -+# CONFIG_CLK_QORIQ is not set -+# CONFIG_COMMON_CLK_XGENE is not set -+# CONFIG_COMMON_CLK_PWM is not set -+# CONFIG_COMMON_CLK_VC5 is not set -+# CONFIG_COMMON_CLK_BD718XX is not set -+# CONFIG_COMMON_CLK_FIXED_MMIO is not set - CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING=y -+# CONFIG_COMMON_CLK_BEGIN_ACCOUNTING_FROM_BOOT is not set -+CONFIG_CLK_TEGRA_BPMP=y -+CONFIG_TEGRA_CLK_DFLL=y -+CONFIG_TEGRA_CLK_DEBUG=y -+# CONFIG_HWSPINLOCK is not set -+ -+# -+# Clock Source drivers -+# -+CONFIG_TIMER_OF=y -+CONFIG_TIMER_ACPI=y -+CONFIG_TIMER_PROBE=y -+CONFIG_CLKSRC_MMIO=y -+CONFIG_TEGRA_TIMER=y -+CONFIG_ARM_ARCH_TIMER=y -+CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y -+CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND=y -+CONFIG_FSL_ERRATUM_A008585=y -+CONFIG_HISILICON_ERRATUM_161010101=y -+CONFIG_ARM64_ERRATUM_858921=y -+# CONFIG_MICROCHIP_PIT64B is not set -+# end of Clock Source drivers -+ - CONFIG_CLK_SRC_TEGRA18_TIMER=y -+CONFIG_MAILBOX=y - CONFIG_ARM_MHU=m -+# CONFIG_PLATFORM_MHU is not set -+# CONFIG_PL320_MBOX is not set -+CONFIG_PCC=y -+# CONFIG_ALTERA_MBOX is not set -+# CONFIG_MAILBOX_TEST is not set -+CONFIG_TEGRA_HSP_MBOX=y -+CONFIG_IOMMU_IOVA=y -+CONFIG_IOMMU_API=y -+CONFIG_IOMMU_SUPPORT=y -+ -+# -+# Generic IOMMU Pagetable Support -+# -+CONFIG_IOMMU_IO_PGTABLE=y -+CONFIG_IOMMU_IO_PGTABLE_LPAE=y -+# CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST is not set -+# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set -+# end of Generic IOMMU Pagetable Support -+ -+# CONFIG_IOMMU_DEBUGFS is not set -+# CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set -+CONFIG_OF_IOMMU=y -+CONFIG_IOMMU_DMA=y - CONFIG_TEGRA_IOMMU_SMMU=y - CONFIG_ARM_SMMU=y - CONFIG_ARM_SMMU_DEBUG=y -+# CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS is not set -+CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT=y - CONFIG_ARM_SMMU_V3=y -+# CONFIG_ARM_SMMU_V3_SVA is not set -+# CONFIG_VIRTIO_IOMMU is not set -+CONFIG_ARM_SMMU_SUSPEND=y -+ -+# -+# Remoteproc drivers -+# -+# CONFIG_REMOTEPROC is not set -+# end of Remoteproc drivers -+ -+# -+# Rpmsg drivers -+# -+# CONFIG_RPMSG_QCOM_GLINK_RPM is not set -+# CONFIG_RPMSG_VIRTIO is not set -+# end of Rpmsg drivers -+ - CONFIG_SOUNDWIRE=m -+ -+# -+# SoundWire Devices -+# -+# CONFIG_SOUNDWIRE_INTEL is not set -+# CONFIG_SOUNDWIRE_QCOM is not set -+ -+# -+# SOC (System On Chip) specific Drivers -+# -+ -+# -+# Amlogic SoC drivers -+# -+# end of Amlogic SoC drivers -+ -+# -+# Aspeed SoC drivers -+# -+# end of Aspeed SoC drivers -+ -+# -+# Broadcom SoC drivers -+# -+# CONFIG_SOC_BRCMSTB is not set -+# end of Broadcom SoC drivers -+ -+# -+# NXP/Freescale QorIQ SoC drivers -+# -+# CONFIG_QUICC_ENGINE is not set -+# CONFIG_FSL_RCPM is not set -+# end of NXP/Freescale QorIQ SoC drivers -+ -+# -+# i.MX SoC drivers -+# -+# end of i.MX SoC drivers -+ -+# -+# Qualcomm SoC drivers -+# -+# end of Qualcomm SoC drivers -+ -+# CONFIG_ARCH_TEGRA_132_SOC is not set - CONFIG_ARCH_TEGRA_210_SOC=y - CONFIG_ARCH_TEGRA_186_SOC=y - CONFIG_ARCH_TEGRA_194_SOC=y - CONFIG_ARCH_TEGRA_234_SOC=y -+CONFIG_SOC_TEGRA_FUSE=y -+CONFIG_SOC_TEGRA_FLOWCTRL=y -+CONFIG_SOC_TEGRA_PMC=y -+CONFIG_SOC_TEGRA_POWERGATE_BPMP=y -+CONFIG_TEGRA_USE_NA_GPCPLL=y -+CONFIG_SOC_TEGRA_CBB=y - CONFIG_TEGRA_KFUSE=y -+# CONFIG_TEGRA_FUSE_DOWNSTREAM is not set - CONFIG_TEGRA_FUSE_BURN=y -+# CONFIG_TEGRA_PROC_POWER_MODEL is not set -+CONFIG_TEGRA_DVFS=y - CONFIG_TEGRA_210_DVFS=y -+# CONFIG_SOC_TI is not set -+ -+# -+# Xilinx SoC drivers -+# -+# CONFIG_XILINX_VCU is not set -+# end of Xilinx SoC drivers -+# end of SOC (System On Chip) specific Drivers -+ -+CONFIG_PM_DEVFREQ=y -+ -+# -+# DEVFREQ Governors -+# -+CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y - CONFIG_DEVFREQ_GOV_PERFORMANCE=y -+# CONFIG_DEVFREQ_GOV_POWERSAVE is not set - CONFIG_DEVFREQ_GOV_USERSPACE=y -+# CONFIG_DEVFREQ_GOV_PASSIVE is not set -+ -+# -+# DEVFREQ Drivers -+# -+# CONFIG_ARM_TEGRA_DEVFREQ is not set -+# CONFIG_PM_DEVFREQ_EVENT is not set -+ -+# -+# NVIDIA DEVFREQ Governors -+# -+# CONFIG_DEVFREQ_GOV_POD_SCALING is not set - CONFIG_DEVFREQ_GOV_POD_SCALING_V2=y -+CONFIG_DEVFREQ_GOV_POD_SCALING_HISTORY_BUFFER_SIZE_MAX=100 -+# CONFIG_DEVFREQ_GOV_WMARK_SIMPLE is not set - CONFIG_DEVFREQ_GOV_WMARK_ACTIVE=y -+CONFIG_EXTCON=y -+ -+# -+# Extcon Device Drivers -+# -+# CONFIG_EXTCON_ADC_JACK is not set -+# CONFIG_EXTCON_FSA9480 is not set - CONFIG_EXTCON_GPIO=y -+# CONFIG_EXTCON_MAX3355 is not set -+# CONFIG_EXTCON_CABLE_XLATE is not set -+# CONFIG_EXTCON_PTN5150 is not set -+# CONFIG_EXTCON_RT8973A is not set -+# CONFIG_EXTCON_SM5502 is not set - CONFIG_EXTCON_USB_GPIO=y -+CONFIG_EXTCON_DISP_STATE=y - CONFIG_MEMORY=y -+# CONFIG_ARM_PL172_MPMC is not set -+CONFIG_TEGRA_MC=y -+# CONFIG_TEGRA210_EMC is not set - CONFIG_IIO=y -+CONFIG_IIO_BUFFER=y -+# CONFIG_IIO_BUFFER_CB is not set -+# CONFIG_IIO_BUFFER_DMA is not set -+# CONFIG_IIO_BUFFER_DMAENGINE is not set -+# CONFIG_IIO_BUFFER_HW_CONSUMER is not set -+CONFIG_IIO_KFIFO_BUF=m -+CONFIG_IIO_TRIGGERED_BUFFER=m -+# CONFIG_IIO_CONFIGFS is not set -+CONFIG_IIO_TRIGGER=y -+CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 -+# CONFIG_IIO_SW_DEVICE is not set -+# CONFIG_IIO_SW_TRIGGER is not set -+# CONFIG_IIO_TRIGGERED_EVENT is not set -+ -+# -+# Accelerometers -+# -+# CONFIG_ADIS16201 is not set -+# CONFIG_ADIS16209 is not set -+# CONFIG_ADXL345_I2C is not set -+# CONFIG_ADXL345_SPI is not set -+# CONFIG_ADXL372_SPI is not set -+# CONFIG_ADXL372_I2C is not set -+# CONFIG_BMA180 is not set -+# CONFIG_BMA220 is not set -+# CONFIG_BMA400 is not set -+# CONFIG_BMC150_ACCEL is not set -+# CONFIG_DA280 is not set -+# CONFIG_DA311 is not set -+# CONFIG_DMARD06 is not set -+# CONFIG_DMARD09 is not set -+# CONFIG_DMARD10 is not set - CONFIG_HID_SENSOR_ACCEL_3D=m -+# CONFIG_IIO_ST_ACCEL_3AXIS is not set -+# CONFIG_KXSD9 is not set -+# CONFIG_KXCJK1013 is not set -+# CONFIG_MC3230 is not set -+# CONFIG_MMA7455_I2C is not set -+# CONFIG_MMA7455_SPI is not set -+# CONFIG_MMA7660 is not set -+# CONFIG_MMA8452 is not set -+# CONFIG_MMA9551 is not set -+# CONFIG_MMA9553 is not set -+# CONFIG_MXC4005 is not set -+# CONFIG_MXC6255 is not set -+# CONFIG_SCA3000 is not set -+# CONFIG_STK8312 is not set -+# CONFIG_STK8BA50 is not set -+# end of Accelerometers -+ -+# -+# Analog to digital converters -+# -+# CONFIG_AD7091R5 is not set -+# CONFIG_AD7124 is not set -+# CONFIG_AD7192 is not set -+# CONFIG_AD7266 is not set -+# CONFIG_AD7291 is not set -+# CONFIG_AD7292 is not set -+# CONFIG_AD7298 is not set -+# CONFIG_AD7476 is not set -+# CONFIG_AD7606_IFACE_PARALLEL is not set -+# CONFIG_AD7606_IFACE_SPI is not set -+# CONFIG_AD7766 is not set -+# CONFIG_AD7768_1 is not set -+# CONFIG_AD7780 is not set -+# CONFIG_AD7791 is not set -+# CONFIG_AD7793 is not set -+# CONFIG_AD7887 is not set -+# CONFIG_AD7923 is not set -+# CONFIG_AD7949 is not set -+# CONFIG_AD799X is not set -+# CONFIG_ADI_AXI_ADC is not set -+# CONFIG_CC10001_ADC is not set -+# CONFIG_ENVELOPE_DETECTOR is not set -+# CONFIG_HI8435 is not set -+# CONFIG_HX711 is not set -+# CONFIG_INA2XX_ADC is not set -+# CONFIG_LTC2471 is not set -+# CONFIG_LTC2485 is not set -+# CONFIG_LTC2496 is not set -+# CONFIG_LTC2497 is not set -+# CONFIG_MAX1027 is not set -+# CONFIG_MAX11100 is not set -+# CONFIG_MAX1118 is not set -+# CONFIG_MAX1241 is not set -+# CONFIG_MAX1363 is not set -+# CONFIG_MAX9611 is not set -+# CONFIG_MCP320X is not set -+# CONFIG_MCP3422 is not set -+# CONFIG_MCP3911 is not set -+# CONFIG_NAU7802 is not set -+CONFIG_QCOM_VADC_COMMON=m -+# CONFIG_QCOM_SPMI_IADC is not set -+# CONFIG_QCOM_SPMI_VADC is not set - CONFIG_QCOM_SPMI_ADC5=m -+# CONFIG_SD_ADC_MODULATOR is not set -+# CONFIG_TI_ADC081C is not set -+# CONFIG_TI_ADC0832 is not set -+# CONFIG_TI_ADC084S021 is not set -+# CONFIG_TI_ADC12138 is not set -+# CONFIG_TI_ADC108S102 is not set -+# CONFIG_TI_ADC128S052 is not set -+# CONFIG_TI_ADC161S626 is not set -+# CONFIG_TI_ADS1015 is not set -+# CONFIG_TI_ADS7950 is not set -+# CONFIG_TI_ADS8344 is not set -+# CONFIG_TI_ADS8688 is not set -+# CONFIG_TI_ADS124S08 is not set -+# CONFIG_TI_TLC4541 is not set -+# CONFIG_VF610_ADC is not set -+# CONFIG_XILINX_XADC is not set -+# end of Analog to digital converters -+ -+# -+# Analog Front Ends -+# -+# CONFIG_IIO_RESCALE is not set -+# end of Analog Front Ends -+ -+# -+# Amplifiers -+# -+# CONFIG_AD8366 is not set -+# CONFIG_HMC425 is not set -+# end of Amplifiers -+ -+# -+# Chemical Sensors -+# -+# CONFIG_ATLAS_PH_SENSOR is not set -+# CONFIG_ATLAS_EZO_SENSOR is not set -+# CONFIG_BME680 is not set -+# CONFIG_CCS811 is not set -+# CONFIG_IAQCORE is not set -+# CONFIG_PMS7003 is not set -+# CONFIG_SCD30_CORE is not set -+# CONFIG_SENSIRION_SGP30 is not set -+# CONFIG_SPS30 is not set -+# CONFIG_VZ89X is not set -+# end of Chemical Sensors -+ -+# -+# Hid Sensor IIO Common -+# -+CONFIG_HID_SENSOR_IIO_COMMON=m -+CONFIG_HID_SENSOR_IIO_TRIGGER=m -+# end of Hid Sensor IIO Common -+ -+# -+# SSP Sensor Common -+# -+# CONFIG_IIO_SSP_SENSORHUB is not set -+# end of SSP Sensor Common -+ -+# -+# Digital to analog converters -+# -+# CONFIG_AD5064 is not set -+# CONFIG_AD5360 is not set -+# CONFIG_AD5380 is not set -+# CONFIG_AD5421 is not set -+# CONFIG_AD5446 is not set -+# CONFIG_AD5449 is not set -+# CONFIG_AD5592R is not set -+# CONFIG_AD5593R is not set -+# CONFIG_AD5504 is not set -+# CONFIG_AD5624R_SPI is not set -+# CONFIG_AD5686_SPI is not set -+# CONFIG_AD5696_I2C is not set -+# CONFIG_AD5755 is not set -+# CONFIG_AD5758 is not set -+# CONFIG_AD5761 is not set -+# CONFIG_AD5764 is not set -+# CONFIG_AD5770R is not set -+# CONFIG_AD5791 is not set -+# CONFIG_AD7303 is not set -+# CONFIG_AD8801 is not set -+# CONFIG_DPOT_DAC is not set - CONFIG_DS4424=m -+# CONFIG_LTC1660 is not set -+# CONFIG_LTC2632 is not set -+# CONFIG_M62332 is not set -+# CONFIG_MAX517 is not set -+# CONFIG_MAX5821 is not set -+# CONFIG_MCP4725 is not set -+# CONFIG_MCP4922 is not set -+# CONFIG_TI_DAC082S085 is not set -+# CONFIG_TI_DAC5571 is not set -+# CONFIG_TI_DAC7311 is not set -+# CONFIG_TI_DAC7612 is not set -+# CONFIG_VF610_DAC is not set -+# end of Digital to analog converters -+ -+# -+# IIO dummy driver -+# -+# end of IIO dummy driver -+ -+# -+# Frequency Synthesizers DDS/PLL -+# -+ -+# -+# Clock Generator/Distribution -+# -+# CONFIG_AD9523 is not set -+# end of Clock Generator/Distribution -+ -+# -+# Phase-Locked Loop (PLL) frequency synthesizers -+# -+# CONFIG_ADF4350 is not set -+# CONFIG_ADF4371 is not set -+# end of Phase-Locked Loop (PLL) frequency synthesizers -+# end of Frequency Synthesizers DDS/PLL -+ -+# -+# Digital gyroscope sensors -+# -+# CONFIG_ADIS16080 is not set -+# CONFIG_ADIS16130 is not set -+# CONFIG_ADIS16136 is not set -+# CONFIG_ADIS16260 is not set -+# CONFIG_ADXRS290 is not set -+# CONFIG_ADXRS450 is not set -+# CONFIG_BMG160 is not set -+# CONFIG_FXAS21002C is not set - CONFIG_HID_SENSOR_GYRO_3D=m -+# CONFIG_MPU3050_I2C is not set -+# CONFIG_IIO_ST_GYRO_3AXIS is not set -+# CONFIG_ITG3200 is not set -+# end of Digital gyroscope sensors -+ -+# -+# Health Sensors -+# -+ -+# -+# Heart Rate Monitors -+# -+# CONFIG_AFE4403 is not set -+# CONFIG_AFE4404 is not set -+# CONFIG_MAX30100 is not set -+# CONFIG_MAX30102 is not set -+# end of Heart Rate Monitors -+# end of Health Sensors -+ -+# -+# Humidity sensors -+# -+# CONFIG_AM2315 is not set -+# CONFIG_DHT11 is not set -+# CONFIG_HDC100X is not set -+# CONFIG_HDC2010 is not set -+# CONFIG_HID_SENSOR_HUMIDITY is not set -+# CONFIG_HTS221 is not set -+# CONFIG_HTU21 is not set -+# CONFIG_SI7005 is not set -+# CONFIG_SI7020 is not set -+# end of Humidity sensors -+ -+# -+# Inertial measurement units -+# -+# CONFIG_ADIS16400 is not set -+# CONFIG_ADIS16460 is not set -+# CONFIG_ADIS16475 is not set -+# CONFIG_ADIS16480 is not set -+# CONFIG_BMI160_I2C is not set -+# CONFIG_BMI160_SPI is not set -+# CONFIG_FXOS8700_I2C is not set -+# CONFIG_FXOS8700_SPI is not set -+# CONFIG_KMX61 is not set -+# CONFIG_INV_ICM42600_I2C is not set -+# CONFIG_INV_ICM42600_SPI is not set -+# CONFIG_INV_MPU6050_I2C is not set -+# CONFIG_INV_MPU6050_SPI is not set -+# CONFIG_IIO_ST_LSM6DSX is not set -+# CONFIG_NVI_MPU_IIO is not set -+# CONFIG_NVI_MPU_INPUT is not set -+# CONFIG_NVI_MPU_RELAY is not set -+# CONFIG_NVS_BMI160_IIO is not set -+# CONFIG_NVS_BMI160_INPUT is not set -+# CONFIG_NVS_BMI160_RELAY is not set -+# CONFIG_NVS_BMI08X_IIO is not set -+# CONFIG_NVS_BMI08X_INPUT is not set -+# CONFIG_NVS_BMI08X_RELAY is not set - CONFIG_BMI088_IIO=m -+# CONFIG_TSFW_ICM is not set -+# end of Inertial measurement units -+ -+# -+# Light sensors -+# -+# CONFIG_ACPI_ALS is not set -+# CONFIG_ADJD_S311 is not set -+# CONFIG_ADUX1020 is not set -+# CONFIG_AL3010 is not set -+# CONFIG_AL3320A is not set -+# CONFIG_APDS9300 is not set -+# CONFIG_APDS9960 is not set -+# CONFIG_AS73211 is not set -+# CONFIG_BH1750 is not set -+# CONFIG_BH1780 is not set -+# CONFIG_CM32181 is not set -+# CONFIG_CM3232 is not set -+# CONFIG_CM3323 is not set -+# CONFIG_CM3605 is not set -+# CONFIG_CM36651 is not set -+# CONFIG_GP2AP002 is not set -+# CONFIG_GP2AP020A00F is not set -+# CONFIG_SENSORS_ISL29018 is not set -+# CONFIG_SENSORS_ISL29028 is not set -+# CONFIG_ISL29125 is not set -+# CONFIG_HID_SENSOR_ALS is not set -+# CONFIG_HID_SENSOR_PROX is not set -+# CONFIG_JSA1212 is not set -+# CONFIG_RPR0521 is not set -+# CONFIG_LTR501 is not set -+# CONFIG_LV0104CS is not set -+# CONFIG_MAX44000 is not set -+# CONFIG_MAX44009 is not set -+# CONFIG_NOA1305 is not set -+# CONFIG_OPT3001 is not set -+# CONFIG_PA12203001 is not set -+# CONFIG_SI1133 is not set -+# CONFIG_SI1145 is not set -+# CONFIG_STK3310 is not set -+# CONFIG_ST_UVIS25 is not set -+# CONFIG_TCS3414 is not set -+# CONFIG_TCS3472 is not set -+# CONFIG_SENSORS_TSL2563 is not set -+# CONFIG_TSL2583 is not set -+# CONFIG_TSL2772 is not set -+# CONFIG_TSL4531 is not set -+# CONFIG_US5182D is not set -+# CONFIG_VCNL4000 is not set -+# CONFIG_VCNL4035 is not set -+# CONFIG_VEML6030 is not set -+# CONFIG_VEML6070 is not set -+# CONFIG_VL6180 is not set -+# CONFIG_ZOPT2201 is not set -+# end of Light sensors -+ -+# -+# Magnetometer sensors -+# -+# CONFIG_AK8974 is not set -+# CONFIG_AK8975 is not set -+# CONFIG_AK09911 is not set -+# CONFIG_BMC150_MAGN_I2C is not set -+# CONFIG_BMC150_MAGN_SPI is not set -+# CONFIG_MAG3110 is not set -+# CONFIG_HID_SENSOR_MAGNETOMETER_3D is not set -+# CONFIG_MMC35240 is not set -+# CONFIG_IIO_ST_MAGN_3AXIS is not set -+# CONFIG_SENSORS_HMC5843_I2C is not set -+# CONFIG_SENSORS_HMC5843_SPI is not set -+# CONFIG_SENSORS_RM3100_I2C is not set -+# CONFIG_SENSORS_RM3100_SPI is not set -+# end of Magnetometer sensors -+ -+# -+# Multiplexers -+# -+# CONFIG_IIO_MUX is not set -+# end of Multiplexers -+ -+# -+# Inclinometer sensors -+# -+# CONFIG_HID_SENSOR_INCLINOMETER_3D is not set -+# CONFIG_HID_SENSOR_DEVICE_ROTATION is not set -+# end of Inclinometer sensors -+ -+# -+# Triggers - standalone -+# -+# CONFIG_IIO_INTERRUPT_TRIGGER is not set -+# CONFIG_IIO_SYSFS_TRIGGER is not set -+# end of Triggers - standalone -+ -+# -+# Linear and angular position sensors -+# -+# end of Linear and angular position sensors -+ -+# -+# Digital potentiometers -+# -+# CONFIG_AD5272 is not set -+# CONFIG_DS1803 is not set -+# CONFIG_MAX5432 is not set -+# CONFIG_MAX5481 is not set -+# CONFIG_MAX5487 is not set -+# CONFIG_MCP4018 is not set -+# CONFIG_MCP4131 is not set -+# CONFIG_MCP4531 is not set -+# CONFIG_MCP41010 is not set -+# CONFIG_TPL0102 is not set -+# end of Digital potentiometers -+ -+# -+# Digital potentiostats -+# -+# CONFIG_LMP91000 is not set -+# end of Digital potentiostats -+ -+# -+# Pressure sensors -+# -+# CONFIG_ABP060MG is not set -+# CONFIG_BMP280 is not set -+# CONFIG_DLHL60D is not set -+# CONFIG_DPS310 is not set -+# CONFIG_HID_SENSOR_PRESS is not set -+# CONFIG_HP03 is not set -+# CONFIG_ICP10100 is not set -+# CONFIG_MPL115_I2C is not set -+# CONFIG_MPL115_SPI is not set -+# CONFIG_MPL3115 is not set -+# CONFIG_MS5611 is not set -+# CONFIG_MS5637 is not set -+# CONFIG_IIO_ST_PRESS is not set -+# CONFIG_T5403 is not set -+# CONFIG_HP206C is not set -+# CONFIG_ZPA2326 is not set -+# end of Pressure sensors -+ -+# -+# Lightning sensors -+# -+# CONFIG_AS3935 is not set -+# end of Lightning sensors -+ -+# -+# Proximity and distance sensors -+# -+# CONFIG_ISL29501 is not set -+# CONFIG_LIDAR_LITE_V2 is not set -+# CONFIG_MB1232 is not set -+# CONFIG_PING is not set -+# CONFIG_RFD77402 is not set -+# CONFIG_SRF04 is not set -+# CONFIG_SX9310 is not set -+# CONFIG_SX9500 is not set -+# CONFIG_SRF08 is not set -+# CONFIG_VCNL3020 is not set -+# CONFIG_VL53L0X_I2C is not set -+# end of Proximity and distance sensors -+ -+# -+# Proximity sensors -+# -+# end of Proximity sensors -+ -+# -+# Resolver to digital converters -+# -+# CONFIG_AD2S90 is not set -+# CONFIG_AD2S1200 is not set -+# end of Resolver to digital converters -+ -+# -+# Temperature sensors -+# -+# CONFIG_LTC2983 is not set -+# CONFIG_MAXIM_THERMOCOUPLE is not set -+# CONFIG_HID_SENSOR_TEMP is not set -+# CONFIG_MLX90614 is not set -+# CONFIG_MLX90632 is not set -+# CONFIG_TMP006 is not set -+# CONFIG_TMP007 is not set -+# CONFIG_TSYS01 is not set -+# CONFIG_TSYS02D is not set -+# CONFIG_MAX31856 is not set -+# CONFIG_NVS_TMP1X2_IIO is not set -+# CONFIG_NVS_TMP1X2_INPUT is not set -+# CONFIG_NVS_TMP1X2_RELAY is not set -+# end of Temperature sensors -+ -+# CONFIG_NVS_LED_TEST is not set - CONFIG_NTB=m -+# CONFIG_NTB_MSI is not set -+# CONFIG_NTB_IDT is not set - CONFIG_NTB_SWITCHTEC=m - CONFIG_NTB_PINGPONG=m - CONFIG_NTB_TOOL=m - CONFIG_NTB_PERF=m - CONFIG_NTB_TRANSPORT=m -+# CONFIG_VME_BUS is not set - CONFIG_PWM=y -+CONFIG_PWM_SYSFS=y -+# CONFIG_PWM_DEBUG is not set -+# CONFIG_PWM_FSL_FTM is not set -+# CONFIG_PWM_PCA9685 is not set - CONFIG_PWM_TEGRA=y -+# CONFIG_PWM_TEGRA_PMC_SOFT_LED_BLINK is not set - CONFIG_PWM_TEGRA_PMC_BLINK=m - CONFIG_PWM_TEGRA_TACHOMETER=y - CONFIG_PWM_TEGRA_DFLL=y -+ -+# -+# IRQ chip support -+# -+CONFIG_IRQCHIP=y -+CONFIG_ARM_GIC=y -+CONFIG_FIQ=y -+CONFIG_ARM_GIC_PM=y -+CONFIG_ARM_GIC_MAX_NR=1 -+CONFIG_ARM_GIC_V2M=y -+CONFIG_ARM_GIC_V3=y -+CONFIG_ARM_GIC_V3_ITS=y -+CONFIG_ARM_GIC_V3_ITS_PCI=y -+# CONFIG_AL_FIC is not set -+CONFIG_PARTITION_PERCPU=y -+# end of IRQ chip support -+ -+# CONFIG_IPACK_BUS is not set -+CONFIG_ARCH_HAS_RESET_CONTROLLER=y -+CONFIG_RESET_CONTROLLER=y -+# CONFIG_RESET_TI_SYSCON is not set -+CONFIG_RESET_TEGRA_BPMP=y -+ -+# -+# PHY Subsystem -+# -+CONFIG_GENERIC_PHY=y -+# CONFIG_PHY_XGENE is not set -+# CONFIG_BCM_KONA_USB2_PHY is not set -+# CONFIG_PHY_CADENCE_TORRENT is not set -+# CONFIG_PHY_CADENCE_DPHY is not set -+# CONFIG_PHY_CADENCE_SIERRA is not set -+# CONFIG_PHY_CADENCE_SALVO is not set - CONFIG_PHY_FSL_IMX8MQ_USB=y -+# CONFIG_PHY_MIXEL_MIPI_DPHY is not set -+# CONFIG_PHY_PXA_28NM_HSIC is not set -+# CONFIG_PHY_PXA_28NM_USB2 is not set -+# CONFIG_PHY_CPCAP_USB is not set -+# CONFIG_PHY_MAPPHONE_MDM6600 is not set -+# CONFIG_PHY_OCELOT_SERDES is not set -+# CONFIG_PHY_QCOM_USB_HS is not set -+# CONFIG_PHY_QCOM_USB_HSIC is not set - CONFIG_PHY_TEGRA_XUSB=y -+CONFIG_PHY_TEGRA194_P2U=y -+# CONFIG_TEGRA_P2U is not set -+# CONFIG_PHY_TUSB1210 is not set -+# end of PHY Subsystem -+ -+# CONFIG_POWERCAP is not set -+# CONFIG_MCB is not set -+ -+# -+# Performance monitor support -+# -+# CONFIG_ARM_CCI_PMU is not set -+# CONFIG_ARM_CCN is not set -+# CONFIG_ARM_CMN is not set -+CONFIG_ARM_PMU=y -+CONFIG_ARM_PMU_ACPI=y - CONFIG_ARM_SMMU_V3_PMU=m - CONFIG_ARM_DSU_PMU=y - CONFIG_ARM_SPE_PMU=y -+# CONFIG_HISI_PMU is not set -+# end of Performance monitor support -+ -+CONFIG_RAS=y -+CONFIG_ARM64_RAS=y -+# CONFIG_USB4 is not set -+ -+# -+# Android -+# -+# CONFIG_ANDROID is not set -+# end of Android -+ -+# CONFIG_LIBNVDIMM is not set - CONFIG_DAX=y -+# CONFIG_DEV_DAX is not set -+CONFIG_NVMEM=y -+CONFIG_NVMEM_SYSFS=y -+# CONFIG_NVMEM_SPMI_SDAM is not set -+ -+# -+# HW tracing support -+# -+# CONFIG_STM is not set -+# CONFIG_INTEL_TH is not set -+# end of HW tracing support -+ - CONFIG_FPGA=y -+# CONFIG_ALTERA_PR_IP_CORE is not set -+# CONFIG_FPGA_MGR_ALTERA_PS_SPI is not set -+# CONFIG_FPGA_MGR_ALTERA_CVP is not set -+# CONFIG_FPGA_MGR_XILINX_SPI is not set -+# CONFIG_FPGA_MGR_ICE40_SPI is not set -+# CONFIG_FPGA_MGR_MACHXO2_SPI is not set - CONFIG_FPGA_BRIDGE=m -+# CONFIG_ALTERA_FREEZE_BRIDGE is not set -+# CONFIG_XILINX_PR_DECOUPLER is not set - CONFIG_FPGA_REGION=m - CONFIG_OF_FPGA_REGION=m -+# CONFIG_FPGA_DFL is not set -+# CONFIG_FSI is not set - CONFIG_TEE=y -+ -+# -+# TEE drivers -+# - CONFIG_OPTEE=y -+CONFIG_OPTEE_SHM_NUM_PRIV_PAGES=1 -+# end of TEE drivers -+ -+CONFIG_PM_OPP=y -+# CONFIG_SIOX is not set -+# CONFIG_SLIMBUS is not set - CONFIG_INTERCONNECT=y - CONFIG_INTERCONNECT_TEGRA=y -+# CONFIG_COUNTER is not set -+# CONFIG_MOST is not set - CONFIG_RTK_BTUSB=m - CONFIG_NVPMODEL_EMC=y - CONFIG_TEGRA_RDMA=m -+ -+# -+# Generic IOMMU Pagetable Support -+# -+# end of Generic IOMMU Pagetable Support -+ - CONFIG_NVPPS=m -+ -+# -+# Trusty -+# -+# CONFIG_TRUSTY is not set -+# end of Trusty -+ - CONFIG_TEGRA_HV_PM_CTL=y - CONFIG_TEGRA_HV_MANAGER=y - CONFIG_TEGRA_VIRTUALIZATION=y -+CONFIG_TEGRA_GPIO_HOST_PROXY=y -+CONFIG_TEGRA_GPIO_GUEST_PROXY=y -+# end of Device Drivers -+ -+# -+# File systems -+# -+CONFIG_DCACHE_WORD_ACCESS=y -+# CONFIG_VALIDATE_FS_PARSER is not set -+CONFIG_FS_IOMAP=y -+# CONFIG_EXT2_FS is not set - CONFIG_EXT3_FS=y -+# CONFIG_EXT3_FS_POSIX_ACL is not set -+# CONFIG_EXT3_FS_SECURITY is not set -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_USE_FOR_EXT2=y - CONFIG_EXT4_FS_POSIX_ACL=y - CONFIG_EXT4_FS_SECURITY=y -+# CONFIG_EXT4_DEBUG is not set -+CONFIG_JBD2=y -+# CONFIG_JBD2_DEBUG is not set -+CONFIG_FS_MBCACHE=y -+# CONFIG_REISERFS_FS is not set -+# CONFIG_JFS_FS is not set -+# CONFIG_XFS_FS is not set -+# CONFIG_GFS2_FS is not set -+# CONFIG_OCFS2_FS is not set - CONFIG_BTRFS_FS=m - CONFIG_BTRFS_FS_POSIX_ACL=y -+# CONFIG_BTRFS_FS_CHECK_INTEGRITY is not set -+# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set -+# CONFIG_BTRFS_DEBUG is not set -+# CONFIG_BTRFS_ASSERT is not set -+# CONFIG_BTRFS_FS_REF_VERIFY is not set -+# CONFIG_NILFS2_FS is not set -+# CONFIG_F2FS_FS is not set -+# CONFIG_FS_DAX is not set -+CONFIG_FS_POSIX_ACL=y -+CONFIG_EXPORTFS=y -+# CONFIG_EXPORTFS_BLOCK_OPS is not set -+CONFIG_FILE_LOCKING=y -+CONFIG_MANDATORY_FILE_LOCKING=y -+# CONFIG_FS_ENCRYPTION is not set -+# CONFIG_FS_VERITY is not set -+CONFIG_FSNOTIFY=y -+CONFIG_DNOTIFY=y -+CONFIG_INOTIFY_USER=y - CONFIG_FANOTIFY=y - CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y - CONFIG_QUOTA=y - CONFIG_QUOTA_NETLINK_INTERFACE=y -+CONFIG_PRINT_QUOTA_WARNING=y -+# CONFIG_QUOTA_DEBUG is not set -+CONFIG_QUOTA_TREE=m -+# CONFIG_QFMT_V1 is not set - CONFIG_QFMT_V2=m -+CONFIG_QUOTACTL=y - CONFIG_AUTOFS4_FS=y -+CONFIG_AUTOFS_FS=y - CONFIG_FUSE_FS=m - CONFIG_CUSE=m --CONFIG_ISO9660_FS=y -+# CONFIG_VIRTIO_FS is not set - CONFIG_OVERLAY_FS=m -+# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set - # CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW is not set -+# CONFIG_OVERLAY_FS_INDEX is not set -+# CONFIG_OVERLAY_FS_XINO_AUTO is not set -+# CONFIG_OVERLAY_FS_METACOPY is not set -+ -+# -+# Caches -+# -+# CONFIG_FSCACHE is not set -+# end of Caches -+ -+# -+# CD-ROM/DVD Filesystems -+# -+CONFIG_ISO9660_FS=y -+# CONFIG_JOLIET is not set -+# CONFIG_ZISOFS is not set -+# CONFIG_UDF_FS is not set -+# end of CD-ROM/DVD Filesystems -+ -+# -+# DOS/FAT/EXFAT/NT Filesystems -+# -+CONFIG_FAT_FS=y - CONFIG_MSDOS_FS=y - CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_CODEPAGE=437 -+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" -+# CONFIG_FAT_DEFAULT_UTF8 is not set - CONFIG_EXFAT_FS=y -+CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" - CONFIG_NTFS_FS=y -+# CONFIG_NTFS_DEBUG is not set - CONFIG_NTFS_RW=y -+# end of DOS/FAT/EXFAT/NT Filesystems -+ -+# -+# Pseudo filesystems -+# -+CONFIG_PROC_FS=y -+# CONFIG_PROC_KCORE is not set -+CONFIG_PROC_VMCORE=y -+# CONFIG_PROC_VMCORE_DEVICE_DUMP is not set -+CONFIG_PROC_SYSCTL=y -+CONFIG_PROC_PAGE_MONITOR=y -+# CONFIG_PROC_CHILDREN is not set -+CONFIG_KERNFS=y -+CONFIG_SYSFS=y - CONFIG_TMPFS=y - CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_TMPFS_XATTR=y -+# CONFIG_TMPFS_INODE64 is not set - CONFIG_HUGETLBFS=y -+CONFIG_HUGETLB_PAGE=y -+CONFIG_MEMFD_CREATE=y -+CONFIG_ARCH_HAS_GIGANTIC_PAGE=y -+CONFIG_CONFIGFS_FS=y - CONFIG_EFIVAR_FS=y -+# end of Pseudo filesystems -+ -+CONFIG_MISC_FILESYSTEMS=y -+# CONFIG_ORANGEFS_FS is not set -+# CONFIG_ADFS_FS is not set -+# CONFIG_AFFS_FS is not set -+# CONFIG_ECRYPT_FS is not set -+# CONFIG_HFS_FS is not set -+# CONFIG_HFSPLUS_FS is not set -+# CONFIG_BEFS_FS is not set -+# CONFIG_BFS_FS is not set -+# CONFIG_EFS_FS is not set -+# CONFIG_JFFS2_FS is not set -+# CONFIG_UBIFS_FS is not set -+# CONFIG_CRAMFS is not set - CONFIG_SQUASHFS=y -+CONFIG_SQUASHFS_FILE_CACHE=y -+# CONFIG_SQUASHFS_FILE_DIRECT is not set -+CONFIG_SQUASHFS_DECOMP_SINGLE=y -+# CONFIG_SQUASHFS_DECOMP_MULTI is not set -+# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set - CONFIG_SQUASHFS_XATTR=y -+CONFIG_SQUASHFS_ZLIB=y - CONFIG_SQUASHFS_LZ4=y - CONFIG_SQUASHFS_LZO=y - CONFIG_SQUASHFS_XZ=y -+# CONFIG_SQUASHFS_ZSTD is not set -+# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set -+# CONFIG_SQUASHFS_EMBEDDED is not set -+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 -+# CONFIG_VXFS_FS is not set -+# CONFIG_MINIX_FS is not set -+# CONFIG_OMFS_FS is not set -+# CONFIG_HPFS_FS is not set -+# CONFIG_QNX4FS_FS is not set -+# CONFIG_QNX6FS_FS is not set -+# CONFIG_ROMFS_FS is not set -+CONFIG_PSTORE=y -+CONFIG_PSTORE_DEFLATE_COMPRESS=y -+# CONFIG_PSTORE_LZO_COMPRESS is not set -+# CONFIG_PSTORE_LZ4_COMPRESS is not set -+# CONFIG_PSTORE_LZ4HC_COMPRESS is not set -+# CONFIG_PSTORE_842_COMPRESS is not set -+# CONFIG_PSTORE_ZSTD_COMPRESS is not set -+CONFIG_PSTORE_COMPRESS=y -+CONFIG_PSTORE_DEFLATE_COMPRESS_DEFAULT=y -+CONFIG_PSTORE_COMPRESS_DEFAULT="deflate" - CONFIG_PSTORE_CONSOLE=y -+# CONFIG_PSTORE_PMSG is not set -+# CONFIG_PSTORE_FTRACE is not set - CONFIG_PSTORE_RAM=m -+CONFIG_PSTORE_ZONE=y -+# CONFIG_SYSV_FS is not set -+# CONFIG_UFS_FS is not set -+# CONFIG_EROFS_FS is not set -+CONFIG_NETWORK_FILESYSTEMS=y - CONFIG_NFS_FS=y -+CONFIG_NFS_V2=y -+CONFIG_NFS_V3=y - CONFIG_NFS_V3_ACL=y - CONFIG_NFS_V4=y -+# CONFIG_NFS_SWAP is not set - CONFIG_NFS_V4_1=y - CONFIG_NFS_V4_2=y -+CONFIG_PNFS_FILE_LAYOUT=y -+CONFIG_PNFS_BLOCK=y -+CONFIG_PNFS_FLEXFILE_LAYOUT=y -+CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="kernel.org" -+# CONFIG_NFS_V4_1_MIGRATION is not set -+CONFIG_NFS_V4_SECURITY_LABEL=y - CONFIG_ROOT_NFS=y -+# CONFIG_NFS_USE_LEGACY_DNS is not set -+CONFIG_NFS_USE_KERNEL_DNS=y - # CONFIG_NFS_DISABLE_UDP_SUPPORT is not set -+# CONFIG_NFS_V4_2_READ_PLUS is not set - CONFIG_NFSD=m -+CONFIG_NFSD_V2_ACL=y - CONFIG_NFSD_V3=y - CONFIG_NFSD_V3_ACL=y -+# CONFIG_NFSD_V4 is not set -+CONFIG_GRACE_PERIOD=y -+CONFIG_LOCKD=y -+CONFIG_LOCKD_V4=y -+CONFIG_NFS_ACL_SUPPORT=y -+CONFIG_NFS_COMMON=y -+CONFIG_SUNRPC=y -+CONFIG_SUNRPC_GSS=y -+CONFIG_SUNRPC_BACKCHANNEL=y -+# CONFIG_SUNRPC_DEBUG is not set -+CONFIG_SUNRPC_XPRT_RDMA=m -+# CONFIG_CEPH_FS is not set - CONFIG_CIFS=m -+# CONFIG_CIFS_STATS2 is not set -+CONFIG_CIFS_ALLOW_INSECURE_LEGACY=y -+# CONFIG_CIFS_WEAK_PW_HASH is not set -+# CONFIG_CIFS_UPCALL is not set -+# CONFIG_CIFS_XATTR is not set -+CONFIG_CIFS_DEBUG=y -+# CONFIG_CIFS_DEBUG2 is not set -+# CONFIG_CIFS_DEBUG_DUMP_KEYS is not set -+# CONFIG_CIFS_DFS_UPCALL is not set -+# CONFIG_CIFS_SMB_DIRECT is not set -+# CONFIG_CODA_FS is not set -+# CONFIG_AFS_FS is not set - CONFIG_9P_FS=m -+# CONFIG_9P_FS_POSIX_ACL is not set -+# CONFIG_9P_FS_SECURITY is not set -+CONFIG_NLS=y -+CONFIG_NLS_DEFAULT="iso8859-1" - CONFIG_NLS_CODEPAGE_437=y -+# CONFIG_NLS_CODEPAGE_737 is not set -+# CONFIG_NLS_CODEPAGE_775 is not set -+# CONFIG_NLS_CODEPAGE_850 is not set -+# CONFIG_NLS_CODEPAGE_852 is not set -+# CONFIG_NLS_CODEPAGE_855 is not set -+# CONFIG_NLS_CODEPAGE_857 is not set -+# CONFIG_NLS_CODEPAGE_860 is not set -+# CONFIG_NLS_CODEPAGE_861 is not set -+# CONFIG_NLS_CODEPAGE_862 is not set -+# CONFIG_NLS_CODEPAGE_863 is not set -+# CONFIG_NLS_CODEPAGE_864 is not set -+# CONFIG_NLS_CODEPAGE_865 is not set -+# CONFIG_NLS_CODEPAGE_866 is not set -+# CONFIG_NLS_CODEPAGE_869 is not set -+# CONFIG_NLS_CODEPAGE_936 is not set -+# CONFIG_NLS_CODEPAGE_950 is not set -+# CONFIG_NLS_CODEPAGE_932 is not set -+# CONFIG_NLS_CODEPAGE_949 is not set -+# CONFIG_NLS_CODEPAGE_874 is not set -+# CONFIG_NLS_ISO8859_8 is not set -+# CONFIG_NLS_CODEPAGE_1250 is not set -+# CONFIG_NLS_CODEPAGE_1251 is not set -+# CONFIG_NLS_ASCII is not set - CONFIG_NLS_ISO8859_1=y -+# CONFIG_NLS_ISO8859_2 is not set -+# CONFIG_NLS_ISO8859_3 is not set -+# CONFIG_NLS_ISO8859_4 is not set -+# CONFIG_NLS_ISO8859_5 is not set -+# CONFIG_NLS_ISO8859_6 is not set -+# CONFIG_NLS_ISO8859_7 is not set -+# CONFIG_NLS_ISO8859_9 is not set -+# CONFIG_NLS_ISO8859_13 is not set -+# CONFIG_NLS_ISO8859_14 is not set -+# CONFIG_NLS_ISO8859_15 is not set -+# CONFIG_NLS_KOI8_R is not set -+# CONFIG_NLS_KOI8_U is not set -+# CONFIG_NLS_MAC_ROMAN is not set -+# CONFIG_NLS_MAC_CELTIC is not set -+# CONFIG_NLS_MAC_CENTEURO is not set -+# CONFIG_NLS_MAC_CROATIAN is not set -+# CONFIG_NLS_MAC_CYRILLIC is not set -+# CONFIG_NLS_MAC_GAELIC is not set -+# CONFIG_NLS_MAC_GREEK is not set -+# CONFIG_NLS_MAC_ICELAND is not set -+# CONFIG_NLS_MAC_INUIT is not set -+# CONFIG_NLS_MAC_ROMANIAN is not set -+# CONFIG_NLS_MAC_TURKISH is not set - CONFIG_NLS_UTF8=m -+# CONFIG_DLM is not set -+# CONFIG_UNICODE is not set -+CONFIG_IO_WQ=y -+# end of File systems -+ -+# -+# Security options -+# -+CONFIG_KEYS=y -+# CONFIG_KEYS_REQUEST_CACHE is not set -+# CONFIG_PERSISTENT_KEYRINGS is not set -+# CONFIG_ENCRYPTED_KEYS is not set -+# CONFIG_KEY_DH_OPERATIONS is not set -+# CONFIG_KEY_NOTIFICATIONS is not set - CONFIG_SECURITY_DMESG_RESTRICT=y - CONFIG_SECURITY=y -+# CONFIG_SECURITYFS is not set - CONFIG_SECURITY_NETWORK=y -+# CONFIG_SECURITY_INFINIBAND is not set -+# CONFIG_SECURITY_NETWORK_XFRM is not set -+# CONFIG_SECURITY_PATH is not set -+CONFIG_LSM_MMAP_MIN_ADDR=32768 -+CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y - CONFIG_HARDENED_USERCOPY=y -+CONFIG_HARDENED_USERCOPY_FALLBACK=y -+# CONFIG_HARDENED_USERCOPY_PAGESPAN is not set - CONFIG_FORTIFY_SOURCE=y -+# CONFIG_STATIC_USERMODEHELPER is not set - CONFIG_SECURITY_SELINUX=y -+# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set -+# CONFIG_SECURITY_SELINUX_DISABLE is not set -+CONFIG_SECURITY_SELINUX_DEVELOP=y -+CONFIG_SECURITY_SELINUX_AVC_STATS=y -+CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=0 -+CONFIG_SECURITY_SELINUX_SIDTAB_HASH_BITS=9 -+CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE=256 -+# CONFIG_SECURITY_SMACK is not set -+# CONFIG_SECURITY_TOMOYO is not set -+# CONFIG_SECURITY_APPARMOR is not set -+# CONFIG_SECURITY_LOADPIN is not set - CONFIG_SECURITY_YAMA=y -+# CONFIG_SECURITY_SAFESETID is not set -+# CONFIG_SECURITY_LOCKDOWN_LSM is not set -+CONFIG_INTEGRITY=y -+# CONFIG_INTEGRITY_SIGNATURE is not set -+CONFIG_INTEGRITY_AUDIT=y -+# CONFIG_IMA is not set -+# CONFIG_EVM is not set -+CONFIG_DEFAULT_SECURITY_SELINUX=y -+# CONFIG_DEFAULT_SECURITY_DAC is not set - CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity,selinux,bpf" -+ -+# -+# Kernel hardening options -+# -+ -+# -+# Memory initialization -+# -+CONFIG_INIT_STACK_NONE=y -+# CONFIG_GCC_PLUGIN_STRUCTLEAK_USER is not set -+# CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF is not set -+# CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL is not set -+# CONFIG_GCC_PLUGIN_STACKLEAK is not set -+# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set -+# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set -+# end of Memory initialization -+# end of Kernel hardening options -+ -+# CONFIG_TRUSTED_LITTLE_KERNEL is not set -+# end of Security options -+ -+CONFIG_XOR_BLOCKS=m -+CONFIG_CRYPTO=y -+ -+# -+# Crypto core or helper -+# -+CONFIG_CRYPTO_ALGAPI=y -+CONFIG_CRYPTO_ALGAPI2=y -+CONFIG_CRYPTO_AEAD=y -+CONFIG_CRYPTO_AEAD2=y -+CONFIG_CRYPTO_SKCIPHER=y -+CONFIG_CRYPTO_SKCIPHER2=y -+CONFIG_CRYPTO_HASH=y -+CONFIG_CRYPTO_HASH2=y -+CONFIG_CRYPTO_RNG=y -+CONFIG_CRYPTO_RNG2=y -+CONFIG_CRYPTO_RNG_DEFAULT=y -+CONFIG_CRYPTO_AKCIPHER2=y -+CONFIG_CRYPTO_AKCIPHER=y -+CONFIG_CRYPTO_KPP2=y -+CONFIG_CRYPTO_KPP=y -+CONFIG_CRYPTO_ACOMP2=y -+CONFIG_CRYPTO_ECDSA=y -+CONFIG_CRYPTO_MANAGER=y -+CONFIG_CRYPTO_MANAGER2=y -+# CONFIG_CRYPTO_USER is not set -+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y -+CONFIG_CRYPTO_GF128MUL=y -+CONFIG_CRYPTO_NULL=y -+CONFIG_CRYPTO_NULL2=y -+# CONFIG_CRYPTO_PCRYPT is not set -+CONFIG_CRYPTO_CRYPTD=m -+CONFIG_CRYPTO_AUTHENC=y - CONFIG_CRYPTO_TEST=m -+CONFIG_CRYPTO_SIMD=m -+CONFIG_CRYPTO_ENGINE=m -+ -+# -+# Public-key cryptography -+# -+CONFIG_CRYPTO_RSA=y - CONFIG_CRYPTO_DH=y -+CONFIG_CRYPTO_ECC=y -+CONFIG_CRYPTO_ECDH=y -+# CONFIG_CRYPTO_ECRDSA is not set -+# CONFIG_CRYPTO_SM2 is not set -+# CONFIG_CRYPTO_CURVE25519 is not set -+ -+# -+# Authenticated Encryption with Associated Data -+# -+CONFIG_CRYPTO_CCM=m -+CONFIG_CRYPTO_GCM=m -+# CONFIG_CRYPTO_CHACHA20POLY1305 is not set -+# CONFIG_CRYPTO_AEGIS128 is not set - CONFIG_CRYPTO_SEQIV=y - CONFIG_CRYPTO_ECHAINIV=y -+ -+# -+# Block modes -+# -+CONFIG_CRYPTO_CBC=y -+# CONFIG_CRYPTO_CFB is not set - CONFIG_CRYPTO_CTR=y -+# CONFIG_CRYPTO_CTS is not set -+CONFIG_CRYPTO_ECB=y -+# CONFIG_CRYPTO_LRW is not set -+# CONFIG_CRYPTO_OFB is not set -+# CONFIG_CRYPTO_PCBC is not set -+CONFIG_CRYPTO_XTS=m -+# CONFIG_CRYPTO_KEYWRAP is not set -+# CONFIG_CRYPTO_ADIANTUM is not set -+CONFIG_CRYPTO_ESSIV=y -+ -+# -+# Hash modes -+# -+CONFIG_CRYPTO_CMAC=y -+CONFIG_CRYPTO_HMAC=y -+# CONFIG_CRYPTO_XCBC is not set -+# CONFIG_CRYPTO_VMAC is not set -+ -+# -+# Digest -+# -+CONFIG_CRYPTO_CRC32C=y -+# CONFIG_CRYPTO_CRC32 is not set -+CONFIG_CRYPTO_XXHASH=m -+CONFIG_CRYPTO_BLAKE2B=m -+# CONFIG_CRYPTO_BLAKE2S is not set -+CONFIG_CRYPTO_CRCT10DIF=y - CONFIG_CRYPTO_GHASH=y -+# CONFIG_CRYPTO_POLY1305 is not set -+CONFIG_CRYPTO_MD4=m -+CONFIG_CRYPTO_MD5=m -+CONFIG_CRYPTO_MICHAEL_MIC=m -+# CONFIG_CRYPTO_RMD128 is not set -+# CONFIG_CRYPTO_RMD160 is not set -+# CONFIG_CRYPTO_RMD256 is not set -+# CONFIG_CRYPTO_RMD320 is not set -+CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_SHA256=y -+CONFIG_CRYPTO_SHA512=y -+CONFIG_CRYPTO_SHA3=m -+CONFIG_CRYPTO_SM3=m -+# CONFIG_CRYPTO_STREEBOG is not set -+# CONFIG_CRYPTO_TGR192 is not set -+# CONFIG_CRYPTO_WP512 is not set -+ -+# -+# Ciphers -+# -+CONFIG_CRYPTO_AES=y -+# CONFIG_CRYPTO_AES_TI is not set -+# CONFIG_CRYPTO_ANUBIS is not set - CONFIG_CRYPTO_ARC4=y -+# CONFIG_CRYPTO_BLOWFISH is not set -+# CONFIG_CRYPTO_CAMELLIA is not set -+# CONFIG_CRYPTO_CAST5 is not set -+# CONFIG_CRYPTO_CAST6 is not set - CONFIG_CRYPTO_DES=m -+# CONFIG_CRYPTO_FCRYPT is not set -+# CONFIG_CRYPTO_KHAZAD is not set -+# CONFIG_CRYPTO_SALSA20 is not set -+# CONFIG_CRYPTO_CHACHA20 is not set -+# CONFIG_CRYPTO_SEED is not set -+# CONFIG_CRYPTO_SERPENT is not set -+CONFIG_CRYPTO_SM4=m -+# CONFIG_CRYPTO_TEA is not set - CONFIG_CRYPTO_TWOFISH=m -+CONFIG_CRYPTO_TWOFISH_COMMON=m -+ -+# -+# Compression -+# -+CONFIG_CRYPTO_DEFLATE=y -+CONFIG_CRYPTO_LZO=m -+# CONFIG_CRYPTO_842 is not set -+# CONFIG_CRYPTO_LZ4 is not set -+# CONFIG_CRYPTO_LZ4HC is not set -+# CONFIG_CRYPTO_ZSTD is not set -+ -+# -+# Random Number Generation -+# - CONFIG_CRYPTO_ANSI_CPRNG=m -+CONFIG_CRYPTO_DRBG_MENU=y -+CONFIG_CRYPTO_DRBG_HMAC=y -+# CONFIG_CRYPTO_DRBG_HASH is not set -+# CONFIG_CRYPTO_DRBG_CTR is not set -+CONFIG_CRYPTO_DRBG=y -+CONFIG_CRYPTO_JITTERENTROPY=y -+CONFIG_CRYPTO_USER_API=y -+# CONFIG_CRYPTO_USER_API_HASH is not set - CONFIG_CRYPTO_USER_API_SKCIPHER=y - CONFIG_CRYPTO_USER_API_RNG=m -+# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set -+# CONFIG_CRYPTO_USER_API_AEAD is not set -+CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE=y -+CONFIG_CRYPTO_HASH_INFO=y -+ -+# -+# Crypto library routines -+# -+CONFIG_CRYPTO_LIB_AES=y -+CONFIG_CRYPTO_LIB_ARC4=y -+# CONFIG_CRYPTO_LIB_BLAKE2S is not set -+# CONFIG_CRYPTO_LIB_CHACHA is not set -+# CONFIG_CRYPTO_LIB_CURVE25519 is not set -+CONFIG_CRYPTO_LIB_DES=m -+CONFIG_CRYPTO_LIB_POLY1305_RSIZE=9 -+# CONFIG_CRYPTO_LIB_POLY1305 is not set -+# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set -+CONFIG_CRYPTO_LIB_SHA256=y -+CONFIG_CRYPTO_HW=y -+# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set -+# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set -+# CONFIG_CRYPTO_DEV_CCP is not set -+# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set -+# CONFIG_CRYPTO_DEV_CAVIUM_ZIP is not set -+# CONFIG_CRYPTO_DEV_CHELSIO is not set -+CONFIG_CRYPTO_DEV_VIRTIO=m -+# CONFIG_CRYPTO_DEV_SAFEXCEL is not set - CONFIG_CRYPTO_DEV_CCREE=m -+# CONFIG_CRYPTO_DEV_HISI_SEC is not set -+# CONFIG_CRYPTO_DEV_HISI_SEC2 is not set -+# CONFIG_CRYPTO_DEV_HISI_ZIP is not set -+# CONFIG_CRYPTO_DEV_HISI_HPRE is not set -+# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set - CONFIG_TEGRA_CRYPTO_DEV=y - CONFIG_CRYPTO_DEV_TEGRA_SE=y - CONFIG_CRYPTO_DEV_TEGRA_ELLIPTIC_SE=y -@@ -1353,28 +8144,406 @@ CONFIG_CRYPTO_DEV_TEGRA_SE_USE_HOST1X_INTERFACE=y - CONFIG_CRYPTO_DEV_TEGRA_VIRTUAL_SE_INTERFACE=y - CONFIG_CRYPTO_DEV_TEGRA_SE_NVRNG=y - CONFIG_CRYPTO_DEV_TEGRA_NVVSE=y --CONFIG_CRYPTO_DEV_TEGRA_FDE=m -+CONFIG_ASYMMETRIC_KEY_TYPE=y -+CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y -+CONFIG_X509_CERTIFICATE_PARSER=y -+# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set -+CONFIG_PKCS7_MESSAGE_PARSER=y -+# CONFIG_PKCS7_TEST_KEY is not set -+# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set -+ -+# -+# Certificates for signature checking -+# -+CONFIG_MODULE_SIG_KEY="certs/signing_key.pem" -+CONFIG_SYSTEM_TRUSTED_KEYRING=y -+CONFIG_SYSTEM_TRUSTED_KEYS="" -+# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set -+# CONFIG_SECONDARY_TRUSTED_KEYRING is not set -+# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set -+# end of Certificates for signature checking -+ -+CONFIG_BINARY_PRINTF=y -+ -+# -+# Library routines -+# -+CONFIG_RAID6_PQ=m - # CONFIG_RAID6_PQ_BENCHMARK is not set -+CONFIG_LINEAR_RANGES=y -+CONFIG_PACKING=y -+CONFIG_BITREVERSE=y -+CONFIG_HAVE_ARCH_BITREVERSE=y -+CONFIG_GENERIC_STRNCPY_FROM_USER=y -+CONFIG_GENERIC_STRNLEN_USER=y -+CONFIG_GENERIC_NET_UTILS=y -+CONFIG_CORDIC=m -+# CONFIG_PRIME_NUMBERS is not set -+CONFIG_RATIONAL=y -+CONFIG_GENERIC_PCI_IOMAP=y -+CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y -+CONFIG_ARCH_HAS_FAST_MULTIPLIER=y -+CONFIG_ARCH_USE_SYM_ANNOTATIONS=y -+# CONFIG_INDIRECT_PIO is not set -+CONFIG_CRC_CCITT=y -+CONFIG_CRC16=y -+CONFIG_CRC_T10DIF=y -+CONFIG_CRC_ITU_T=m -+CONFIG_CRC32=y -+# CONFIG_CRC32_SELFTEST is not set -+CONFIG_CRC32_SLICEBY8=y -+# CONFIG_CRC32_SLICEBY4 is not set -+# CONFIG_CRC32_SARWATE is not set -+# CONFIG_CRC32_BIT is not set -+# CONFIG_CRC64 is not set -+# CONFIG_CRC4 is not set -+CONFIG_CRC7=m -+CONFIG_LIBCRC32C=m -+# CONFIG_CRC8 is not set -+CONFIG_XXHASH=y -+CONFIG_AUDIT_GENERIC=y -+CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y -+CONFIG_AUDIT_COMPAT_GENERIC=y -+# CONFIG_RANDOM32_SELFTEST is not set -+CONFIG_ZLIB_INFLATE=y -+CONFIG_ZLIB_DEFLATE=y -+CONFIG_LZO_COMPRESS=m -+CONFIG_LZO_DECOMPRESS=y -+CONFIG_LZ4_DECOMPRESS=y -+CONFIG_ZSTD_COMPRESS=m -+CONFIG_ZSTD_DECOMPRESS=y -+CONFIG_XZ_DEC=y -+CONFIG_XZ_DEC_X86=y -+CONFIG_XZ_DEC_POWERPC=y -+CONFIG_XZ_DEC_IA64=y -+CONFIG_XZ_DEC_ARM=y -+CONFIG_XZ_DEC_ARMTHUMB=y -+CONFIG_XZ_DEC_SPARC=y -+CONFIG_XZ_DEC_BCJ=y -+# CONFIG_XZ_DEC_TEST is not set -+CONFIG_DECOMPRESS_GZIP=y -+CONFIG_DECOMPRESS_BZIP2=y -+CONFIG_DECOMPRESS_LZMA=y -+CONFIG_DECOMPRESS_XZ=y -+CONFIG_DECOMPRESS_LZO=y -+CONFIG_DECOMPRESS_LZ4=y -+CONFIG_DECOMPRESS_ZSTD=y -+CONFIG_GENERIC_ALLOCATOR=y -+CONFIG_REED_SOLOMON=m -+CONFIG_REED_SOLOMON_ENC8=y -+CONFIG_REED_SOLOMON_DEC8=y -+CONFIG_TEXTSEARCH=y -+CONFIG_TEXTSEARCH_KMP=m -+CONFIG_TEXTSEARCH_BM=m -+CONFIG_TEXTSEARCH_FSM=m -+CONFIG_INTERVAL_TREE=y -+CONFIG_XARRAY_MULTI=y -+CONFIG_ASSOCIATIVE_ARRAY=y -+CONFIG_HAS_IOMEM=y -+CONFIG_HAS_IOPORT_MAP=y -+CONFIG_HAS_DMA=y -+CONFIG_DMA_OPS=y -+CONFIG_NEED_SG_DMA_LENGTH=y -+CONFIG_NEED_DMA_MAP_STATE=y -+CONFIG_ARCH_DMA_ADDR_T_64BIT=y -+CONFIG_DMA_DECLARE_COHERENT=y -+CONFIG_ARCH_HAS_SETUP_DMA_OPS=y -+CONFIG_ARCH_HAS_TEARDOWN_DMA_OPS=y -+CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE=y -+CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU=y -+CONFIG_ARCH_HAS_DMA_PREP_COHERENT=y -+CONFIG_SWIOTLB=y -+CONFIG_DMA_NONCOHERENT_MMAP=y -+CONFIG_DMA_COHERENT_POOL=y -+CONFIG_DMA_REMAP=y -+CONFIG_DMA_DIRECT_REMAP=y - CONFIG_DMA_CMA=y -+# CONFIG_DMA_PERNUMA_CMA is not set -+ -+# -+# Default contiguous memory area size: -+# - CONFIG_CMA_SIZE_MBYTES=64 -+CONFIG_CMA_SIZE_SEL_MBYTES=y -+# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set -+# CONFIG_CMA_SIZE_SEL_MIN is not set -+# CONFIG_CMA_SIZE_SEL_MAX is not set - CONFIG_CMA_ALIGNMENT=9 -+# CONFIG_DMA_API_DEBUG is not set -+CONFIG_SGL_ALLOC=y -+CONFIG_CPU_RMAP=y -+CONFIG_DQL=y -+CONFIG_GLOB=y -+# CONFIG_GLOB_SELFTEST is not set -+CONFIG_NLATTR=y -+CONFIG_CLZ_TAB=y -+CONFIG_IRQ_POLL=y -+CONFIG_MPILIB=y -+CONFIG_DIMLIB=y -+CONFIG_LIBFDT=y -+CONFIG_OID_REGISTRY=y -+CONFIG_UCS2_STRING=y -+CONFIG_HAVE_GENERIC_VDSO=y -+CONFIG_GENERIC_GETTIMEOFDAY=y -+CONFIG_GENERIC_VDSO_TIME_NS=y -+CONFIG_FONT_SUPPORT=y -+# CONFIG_FONTS is not set -+CONFIG_FONT_8x8=y -+CONFIG_FONT_8x16=y -+CONFIG_SG_POOL=y -+CONFIG_ARCH_STACKWALK=y -+CONFIG_SBITMAP=y -+CONFIG_PARMAN=m -+CONFIG_OBJAGG=m -+# CONFIG_STRING_SELFTEST is not set -+# end of Library routines -+ -+CONFIG_PLDMFW=y -+ -+# -+# Kernel hacking -+# -+ -+# -+# printk and dmesg options -+# - CONFIG_PRINTK_TIME=y -+# CONFIG_PRINTK_CALLER is not set -+CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7 -+CONFIG_CONSOLE_LOGLEVEL_QUIET=4 -+CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 -+# CONFIG_BOOT_PRINTK_DELAY is not set -+# CONFIG_DYNAMIC_DEBUG is not set - CONFIG_DYNAMIC_DEBUG_CORE=y - # CONFIG_SYMBOLIC_ERRNAME is not set -+CONFIG_DEBUG_BUGVERBOSE=y -+# end of printk and dmesg options -+ -+# -+# Compile-time checks and compiler options -+# - CONFIG_DEBUG_INFO=y -+# CONFIG_DEBUG_INFO_REDUCED is not set -+# CONFIG_DEBUG_INFO_COMPRESSED is not set -+# CONFIG_DEBUG_INFO_SPLIT is not set -+# CONFIG_DEBUG_INFO_DWARF4 is not set -+# CONFIG_DEBUG_INFO_BTF is not set -+# CONFIG_GDB_SCRIPTS is not set -+CONFIG_ENABLE_MUST_CHECK=y - CONFIG_FRAME_WARN=4096 -+# CONFIG_STRIP_ASM_SYMS is not set -+# CONFIG_READABLE_ASM is not set -+# CONFIG_HEADERS_INSTALL is not set -+# CONFIG_DEBUG_SECTION_MISMATCH is not set - # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set -+# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_32B is not set -+CONFIG_ARCH_WANT_FRAME_POINTERS=y -+CONFIG_FRAME_POINTER=y -+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set -+# end of Compile-time checks and compiler options -+ -+# -+# Generic Kernel Debugging Instruments -+# - CONFIG_MAGIC_SYSRQ=y -+CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 -+CONFIG_MAGIC_SYSRQ_SERIAL=y -+CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE="" - CONFIG_DEBUG_FS=y -+CONFIG_DEBUG_FS_ALLOW_ALL=y -+# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set -+# CONFIG_DEBUG_FS_ALLOW_NONE is not set -+CONFIG_HAVE_ARCH_KGDB=y -+# CONFIG_KGDB is not set -+CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y -+# CONFIG_UBSAN is not set -+# end of Generic Kernel Debugging Instruments -+ -+CONFIG_DEBUG_KERNEL=y - # CONFIG_DEBUG_MISC is not set -+ -+# -+# Memory Debugging -+# -+# CONFIG_PAGE_EXTENSION is not set -+# CONFIG_DEBUG_PAGEALLOC is not set -+# CONFIG_PAGE_OWNER is not set -+# CONFIG_PAGE_POISONING is not set -+# CONFIG_DEBUG_PAGE_REF is not set -+# CONFIG_DEBUG_RODATA_TEST is not set -+CONFIG_ARCH_HAS_DEBUG_WX=y -+# CONFIG_DEBUG_WX is not set -+CONFIG_GENERIC_PTDUMP=y -+# CONFIG_PTDUMP_DEBUGFS is not set -+# CONFIG_DEBUG_OBJECTS is not set -+# CONFIG_SLUB_DEBUG_ON is not set -+# CONFIG_SLUB_STATS is not set -+CONFIG_HAVE_DEBUG_KMEMLEAK=y -+# CONFIG_DEBUG_KMEMLEAK is not set -+# CONFIG_DEBUG_STACK_USAGE is not set -+# CONFIG_SCHED_STACK_END_CHECK is not set -+CONFIG_ARCH_HAS_DEBUG_VM_PGTABLE=y -+# CONFIG_DEBUG_VM is not set -+# CONFIG_DEBUG_VM_PGTABLE is not set -+CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y -+# CONFIG_DEBUG_VIRTUAL is not set -+# CONFIG_DEBUG_MEMORY_INIT is not set -+# CONFIG_DEBUG_PER_CPU_MAPS is not set -+CONFIG_HAVE_ARCH_KASAN=y -+CONFIG_HAVE_ARCH_KASAN_SW_TAGS=y -+CONFIG_CC_HAS_KASAN_GENERIC=y -+CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y -+# CONFIG_KASAN is not set -+# end of Memory Debugging -+ -+# CONFIG_DEBUG_SHIRQ is not set -+ -+# -+# Debug Oops, Lockups and Hangs -+# - CONFIG_PANIC_ON_OOPS=y -+CONFIG_PANIC_ON_OOPS_VALUE=1 -+CONFIG_PANIC_TIMEOUT=0 -+CONFIG_LOCKUP_DETECTOR=y - CONFIG_SOFTLOCKUP_DETECTOR=y - CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y -+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1 -+CONFIG_DETECT_HUNG_TASK=y -+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 -+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set -+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 - CONFIG_WQ_WATCHDOG=y -+# CONFIG_TEST_LOCKUP is not set -+# end of Debug Oops, Lockups and Hangs -+ -+# -+# Scheduler Debugging -+# -+CONFIG_SCHED_DEBUG=y -+CONFIG_SCHED_INFO=y - CONFIG_SCHEDSTATS=y -+# end of Scheduler Debugging -+ -+# CONFIG_DEBUG_TIMEKEEPING is not set - # CONFIG_DEBUG_PREEMPT is not set -+ -+# -+# Lock Debugging (spinlocks, mutexes, etc...) -+# -+CONFIG_LOCK_DEBUGGING_SUPPORT=y -+# CONFIG_PROVE_LOCKING is not set -+# CONFIG_LOCK_STAT is not set -+# CONFIG_DEBUG_RT_MUTEXES is not set -+# CONFIG_DEBUG_SPINLOCK is not set -+# CONFIG_DEBUG_MUTEXES is not set -+# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set -+# CONFIG_DEBUG_RWSEMS is not set -+# CONFIG_DEBUG_LOCK_ALLOC is not set -+# CONFIG_DEBUG_ATOMIC_SLEEP is not set -+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set -+# CONFIG_LOCK_TORTURE_TEST is not set -+# CONFIG_WW_MUTEX_SELFTEST is not set -+# CONFIG_SCF_TORTURE_TEST is not set -+# CONFIG_CSD_LOCK_WAIT_DEBUG is not set -+# end of Lock Debugging (spinlocks, mutexes, etc...) -+ -+CONFIG_STACKTRACE=y -+# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set -+# CONFIG_DEBUG_KOBJECT is not set -+CONFIG_HAVE_DEBUG_BUGVERBOSE=y -+ -+# -+# Debug kernel data structures -+# -+# CONFIG_DEBUG_LIST is not set -+# CONFIG_DEBUG_PLIST is not set -+# CONFIG_DEBUG_SG is not set -+# CONFIG_DEBUG_NOTIFIERS is not set -+# CONFIG_BUG_ON_DATA_CORRUPTION is not set -+# end of Debug kernel data structures -+ -+# CONFIG_DEBUG_CREDENTIALS is not set -+ -+# -+# RCU Debugging -+# -+# CONFIG_RCU_SCALE_TEST is not set -+# CONFIG_RCU_TORTURE_TEST is not set -+# CONFIG_RCU_REF_SCALE_TEST is not set -+CONFIG_RCU_CPU_STALL_TIMEOUT=21 -+CONFIG_RCU_TRACE=y -+# CONFIG_RCU_EQS_DEBUG is not set -+# end of RCU Debugging -+ -+# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set -+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set -+# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set -+# CONFIG_LATENCYTOP is not set -+CONFIG_NOP_TRACER=y -+CONFIG_HAVE_FUNCTION_TRACER=y -+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y -+CONFIG_HAVE_DYNAMIC_FTRACE=y -+CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y -+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y -+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y -+CONFIG_HAVE_C_RECORDMCOUNT=y -+CONFIG_TRACE_CLOCK=y -+CONFIG_RING_BUFFER=y -+CONFIG_EVENT_TRACING=y -+CONFIG_CONTEXT_SWITCH_TRACER=y -+CONFIG_TRACING=y -+CONFIG_GENERIC_TRACER=y -+CONFIG_TRACING_SUPPORT=y -+CONFIG_FTRACE=y -+# CONFIG_BOOTTIME_TRACING is not set -+CONFIG_FUNCTION_TRACER=y -+CONFIG_FUNCTION_GRAPH_TRACER=y - # CONFIG_DYNAMIC_FTRACE is not set -+# CONFIG_FUNCTION_PROFILER is not set - CONFIG_STACK_TRACER=y -+# CONFIG_IRQSOFF_TRACER is not set -+# CONFIG_PREEMPT_TRACER is not set -+# CONFIG_SCHED_TRACER is not set -+# CONFIG_HWLAT_TRACER is not set -+# CONFIG_FTRACE_SYSCALLS is not set -+# CONFIG_TRACER_SNAPSHOT is not set -+CONFIG_BRANCH_PROFILE_NONE=y -+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set -+# CONFIG_BLK_DEV_IO_TRACE is not set - # CONFIG_UPROBE_EVENTS is not set -+# CONFIG_SYNTH_EVENTS is not set -+# CONFIG_HIST_TRIGGERS is not set -+# CONFIG_TRACE_EVENT_INJECT is not set -+# CONFIG_TRACEPOINT_BENCHMARK is not set -+# CONFIG_RING_BUFFER_BENCHMARK is not set -+# CONFIG_TRACE_EVAL_MAP_FILE is not set -+# CONFIG_FTRACE_STARTUP_TEST is not set -+# CONFIG_RING_BUFFER_STARTUP_TEST is not set -+# CONFIG_PREEMPTIRQ_DELAY_TEST is not set -+# CONFIG_SAMPLES is not set -+CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y -+CONFIG_STRICT_DEVMEM=y -+# CONFIG_IO_STRICT_DEVMEM is not set -+ -+# -+# arm64 Debugging -+# - CONFIG_PID_IN_CONTEXTIDR=y -+# CONFIG_DEBUG_EFI is not set -+# CONFIG_ARM64_RELOC_TEST is not set -+# CONFIG_CORESIGHT is not set -+# end of arm64 Debugging -+ -+# -+# Kernel Testing and Coverage -+# -+# CONFIG_KUNIT is not set -+# CONFIG_NOTIFIER_ERROR_INJECTION is not set -+# CONFIG_FAULT_INJECTION is not set -+CONFIG_ARCH_HAS_KCOV=y -+CONFIG_CC_HAS_SANCOV_TRACE_PC=y -+# CONFIG_KCOV is not set - # CONFIG_RUNTIME_TESTING_MENU is not set -+# CONFIG_MEMTEST is not set -+# end of Kernel Testing and Coverage -+# end of Kernel hacking diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/bpmp-virt-host/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/host/bpmp-virt-host/default.nix index 6910c9833..67f9d3664 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/host/bpmp-virt-host/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/bpmp-virt-host/default.nix @@ -5,9 +5,11 @@ pkgs, config, ... -}: let +}: +let cfg = config.ghaf.hardware.nvidia.virtualization.host.bpmp; -in { +in +{ options.ghaf.hardware.nvidia.virtualization.host.bpmp.enable = lib.mkOption { type = lib.types.bool; default = false; @@ -21,7 +23,7 @@ in { }; config = lib.mkIf cfg.enable { - ghaf.hardware.nvidia.virtualization.enable = true; + nixpkgs.overlays = [ (import ./overlays/qemu) ]; # in practice this configures both host and guest kernel becaue we use only one kernel in the whole system§ boot.kernelPatches = [ @@ -45,5 +47,11 @@ in { }; } ]; + + # TODO: Consider are these really needed, maybe add only in debug builds? + environment.systemPackages = with pkgs; [ + qemu_kvm + dtc + ]; }; } diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix index 6b288952c..d4f05a514 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/default.nix @@ -50,8 +50,8 @@ in { dtsFile = ./gpio_pt_host_overlay.dtso; # Apply overlay only to host passthrough device tree - filter = builtins.trace "Debug dtb filter (gpio-virt-host): tegra234-p3701-0000-p3737-0000.dtb" "tegra234-p3701-0000-p3737-0000.dtb"; - # filter = builtins.trace "Debug dtb filter (gpio-virt-host): tegra234-p3701-host-passthrough.dtb" "tegra234-p3701-host-passthrough.dtb"; + filter = "tegra234-p3701-0000-p3737-0000.dtb"; + # filter = "tegra234-p3701-host-passthrough.dtb"; # filter = "tegra234-p3701-host-passthrough.dtb"; } ]; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/host/uarta-host/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/host/uarta-host/default.nix index 2bbf06656..2b0474b54 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/host/uarta-host/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/host/uarta-host/default.nix @@ -5,9 +5,11 @@ pkgs, config, ... -}: let +}: +let cfg = config.ghaf.hardware.nvidia.passthroughs.host.uarta; -in { +in +{ options.ghaf.hardware.nvidia.passthroughs.host.uarta.enable = lib.mkOption { type = lib.types.bool; default = false; @@ -15,31 +17,35 @@ in { }; config = lib.mkIf cfg.enable { - ghaf.hardware.nvidia.virtualization.enable = true; - ghaf.hardware.nvidia.virtualization.host.bpmp.enable = true; + ghaf.hardware.nvidia.virtualization = { + enable = true; + host.bpmp.enable = true; + }; - systemd.services.enableVfioPlatform = { - description = "Enable the vfio-platform driver for UARTA"; - wantedBy = ["bindSerial3100000.service"]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = "yes"; - ExecStart = '' - ${pkgs.bash}/bin/bash -c "echo vfio-platform > /sys/bus/platform/devices/3100000.serial/driver_override" - ''; + systemd.services = { + enableVfioPlatform = { + description = "Enable the vfio-platform driver for UARTA"; + wantedBy = [ "bindSerial3100000.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + ExecStart = '' + ${pkgs.bash}/bin/bash -c "echo vfio-platform > /sys/bus/platform/devices/3100000.serial/driver_override" + ''; + }; }; - }; - systemd.services.bindSerial3100000 = { - description = "Bind UARTA to the vfio-platform driver"; - wantedBy = ["multi-user.target"]; - after = ["enableVfioPlatform.service"]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = "yes"; - ExecStart = '' - ${pkgs.bash}/bin/bash -c "echo 3100000.serial > /sys/bus/platform/drivers/vfio-platform/bind" - ''; + bindSerial3100000 = { + description = "Bind UARTA to the vfio-platform driver"; + wantedBy = [ "multi-user.target" ]; + after = [ "enableVfioPlatform.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + ExecStart = '' + ${pkgs.bash}/bin/bash -c "echo 3100000.serial > /sys/bus/platform/drivers/vfio-platform/bind" + ''; + }; }; }; }; diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/default.nix index 3f1811644..8517238f6 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/overlays/default.nix @@ -1,11 +1,7 @@ # Copyright 2022-2023 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 (_final: prev: { - qemu = prev.qemu.overrideAttrs (_final: prev: { - patches = - prev.patches - ++ [ - ./patches/0001-qemu-v8.1.3_bpmp-virt.patch - ]; - }); + qemu_kvm = prev.qemu_kvm.overrideAttrs ( + _final: prev: { patches = prev.patches ++ [ ./patches/0001-qemu-v8.1.3_bpmp-virt.patch ]; } + ); }) diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/default.nix index c77905e8e..334032a54 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/passthrough/uarti-net-vm/default.nix @@ -5,9 +5,11 @@ pkgs, config, ... -}: let +}: +let cfg = config.ghaf.hardware.nvidia.passthroughs.uarti_net_vm; -in { +in +{ options.ghaf.hardware.nvidia.passthroughs.uarti_net_vm.enable = lib.mkOption { type = lib.types.bool; default = false; @@ -25,26 +27,28 @@ in { { # Use serial passthrough (ttyAMA0) and virtual PCI serial (ttyS0) # as Linux console - microvm.kernelParams = [ - "console=ttyAMA0 console=ttyS0" - ]; - microvm.qemu.serialConsole = false; - microvm.qemu.extraArgs = [ - # Add custom dtb to Net-VM with 31d0000.serial in platform devices - "-dtb" - "${config.hardware.deviceTree.package}/tegra234-p3701-ghaf-net-vm.dtb" - # Add UARTI (31d0000.serial) as passtrhough device - "-device" - "vfio-platform,host=31d0000.serial" - # Add a virtual PCI serial device as console - "-device" - "pci-serial,chardev=stdio,id=serial0" - ]; + microvm = { + kernelParams = [ "console=ttyAMA0 console=ttyS0" ]; + qemu = { + serialConsole = false; + extraArgs = [ + # Add custom dtb to Net-VM with 31d0000.serial in platform devices + "-dtb" + "${config.hardware.deviceTree.package}/tegra234-p3701-ghaf-net-vm.dtb" + # Add UARTI (31d0000.serial) as passtrhough device + "-device" + "vfio-platform,host=31d0000.serial" + # Add a virtual PCI serial device as console + "-device" + "pci-serial,chardev=stdio,id=serial0" + ]; + }; + }; } ]; # Make sure that Net-VM runs after the binding services are enabled - systemd.services."microvm@net-vm".after = ["bindSerial31d0000.service"]; + systemd.services."microvm@net-vm".after = [ "bindSerial31d0000.service" ]; boot.kernelPatches = [ { @@ -55,7 +59,7 @@ in { systemd.services.bindSerial31d0000 = { description = "Bind UARTI to the vfio-platform driver"; - wantedBy = ["multi-user.target"]; + wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = "yes"; @@ -79,7 +83,7 @@ in { # Apply overlay only to host passthrough device tree # TODO: make this avaliable if PCI passthrough is disabled - filter = builtins.trace "Debug dtb filter (uarti-net-vm): tegra234-p3701-host-passthrough.dtb" "tegra234-p3701-host-passthrough.dtb"; + filter = "tegra234-p3701-host-passthrough.dtb"; } ]; }; diff --git a/modules/jetpack/profiles/debug.nix b/modules/jetpack/profiles/debug.nix index 63009e03e..ec85ad421 100644 --- a/modules/jetpack/profiles/debug.nix +++ b/modules/jetpack/profiles/debug.nix @@ -1,13 +1,11 @@ # Copyright 2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 # -{ - config, - lib, - ... -}: let +{ config, lib, ... }: +let cfg = config.ghaf.profiles.debug; -in { +in +{ config = lib.mkIf cfg.enable { # Enable default accounts and passwords ghaf.hardware.nvidia.orin.optee = { diff --git a/modules/jetpack/profiles/default.nix b/modules/jetpack/profiles/default.nix index 23deb7be2..1fe227f67 100644 --- a/modules/jetpack/profiles/default.nix +++ b/modules/jetpack/profiles/default.nix @@ -1,7 +1,3 @@ # Copyright 2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - imports = [ - ./debug.nix - ]; -} +{ imports = [ ./debug.nix ]; } diff --git a/modules/lanzaboote/default.nix b/modules/lanzaboote/default.nix index 00952d051..ca65483b9 100644 --- a/modules/lanzaboote/default.nix +++ b/modules/lanzaboote/default.nix @@ -6,34 +6,41 @@ lib, pkgs, config, - lanzaboote, ... -}: let +}: +let cfg = config.ghaf.host.secureboot; -in { +in +{ options.ghaf.host.secureboot = { enable = lib.mkEnableOption "Host secureboot"; }; config = lib.mkIf cfg.enable { # To copy demo keys to /etc/secureboot directory - environment.etc.secureboot.source = ./demo-secure-boot-keys; + environment = { + etc.secureboot.source = ./demo-secure-boot-keys; - environment.systemPackages = [ - # For debugging and troubleshooting Secure Boot. - pkgs.sbctl - ]; + systemPackages = [ + # For debugging and troubleshooting Secure Boot. + pkgs.sbctl + ]; + }; # Lanzaboote currently replaces the systemd-boot module. # This setting is usually set to true in configuration.nix # generated at installation time. So we force it to false # for now. - boot.loader.systemd-boot.enable = lib.mkForce false; - boot.loader.efi.canTouchEfiVariables = lib.mkForce false; + boot = { + loader = { + systemd-boot.enable = lib.mkForce false; + efi.canTouchEfiVariables = lib.mkForce false; + }; - boot.lanzaboote = { - enable = true; - pkiBundle = "/etc/secureboot"; + lanzaboote = { + enable = true; + pkiBundle = "/etc/secureboot"; + }; }; }; } diff --git a/modules/lanzaboote/demo-secure-boot-keys/GUID.license b/modules/lanzaboote/demo-secure-boot-keys/GUID.license deleted file mode 100644 index 4c903bea8..000000000 --- a/modules/lanzaboote/demo-secure-boot-keys/GUID.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2022-2023 TII (SSRC) and the Ghaf contributors - -SPDX-License-Identifier: Apache-2.0 diff --git a/modules/microvm/default.nix b/modules/microvm/default.nix deleted file mode 100644 index 933958140..000000000 --- a/modules/microvm/default.nix +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# Implementation of ghaf's virtual machines based on microvm.nix -# -{ - imports = [ - ./virtualization/microvm/microvm-host.nix - ./virtualization/microvm/netvm.nix - ./virtualization/microvm/appvm.nix - ./virtualization/microvm/guivm.nix - ./virtualization/microvm/gpiovm.nix - ./networking.nix - ]; -} diff --git a/modules/microvm/flake-module.nix b/modules/microvm/flake-module.nix new file mode 100644 index 000000000..feb4c5bef --- /dev/null +++ b/modules/microvm/flake-module.nix @@ -0,0 +1,22 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ inputs, ... }: +{ + flake.nixosModules = { + microvm.imports = [ + inputs.microvm.nixosModules.host + (import ./virtualization/microvm/microvm-host.nix { inherit inputs; }) + (import ./virtualization/microvm/netvm.nix { inherit inputs; }) + (import ./virtualization/microvm/gpiovm.nix { inherit inputs; }) + (import ./virtualization/microvm/adminvm.nix { inherit inputs; }) + (import ./virtualization/microvm/appvm.nix { inherit inputs; }) + (import ./virtualization/microvm/guivm.nix { inherit inputs; }) + (import ./virtualization/microvm/audiovm.nix { inherit inputs; }) + ./virtualization/microvm/idsvm/idsvm.nix + ./virtualization/microvm/idsvm/mitmproxy + ./virtualization/microvm/modules.nix + ./networking.nix + ./power-control.nix + ]; + }; +} diff --git a/modules/microvm/networking.nix b/modules/microvm/networking.nix index 589902b3a..01d075856 100644 --- a/modules/microvm/networking.nix +++ b/modules/microvm/networking.nix @@ -3,44 +3,52 @@ { config, lib, + pkgs, ... -}: let +}: +let cfg = config.ghaf.host.networking; + sshKeysHelper = pkgs.callPackage ../../packages/ssh-keys-helper { + inherit pkgs; + inherit config; + }; in - with lib; { - options.ghaf.host.networking = { - enable = mkEnableOption "Host networking"; - # TODO add options to configure the network, e.g. ip addr etc +{ + options.ghaf.host.networking = { + enable = lib.mkEnableOption "Host networking"; + # TODO add options to configure the network, e.g. ip addr etc + }; + + config = lib.mkIf cfg.enable { + networking = { + enableIPv6 = false; + useNetworkd = true; + interfaces.virbr0.useDHCP = false; }; - config = mkIf cfg.enable { - networking = { - enableIPv6 = false; - useNetworkd = true; - interfaces.virbr0.useDHCP = false; + systemd.network = { + netdevs."10-virbr0".netdevConfig = { + Kind = "bridge"; + Name = "virbr0"; + # MACAddress = "02:00:00:02:02:02"; }; - - systemd.network = { - netdevs."10-virbr0".netdevConfig = { - Kind = "bridge"; - Name = "virbr0"; - # MACAddress = "02:00:00:02:02:02"; - }; - networks."10-virbr0" = { - matchConfig.Name = "virbr0"; - networkConfig.DHCPServer = false; - addresses = [ - { - addressConfig.Address = "192.168.101.2/24"; - } - ]; - }; - # Connect VM tun/tap device to the bridge - # TODO configure this based on IF the netvm is enabled - networks."11-netvm" = { - matchConfig.Name = "tap-*"; - networkConfig.Bridge = "virbr0"; - }; + networks."10-virbr0" = { + matchConfig.Name = "virbr0"; + networkConfig.DHCPServer = false; + addresses = [ { Address = "192.168.101.2/24"; } ]; + }; + # Connect VM tun/tap device to the bridge + # TODO configure this based on IF the netvm is enabled + networks."11-netvm" = { + matchConfig.Name = "tap-*"; + networkConfig.Bridge = "virbr0"; }; }; - } + + environment.etc = { + ${config.ghaf.security.sshKeys.getAuthKeysFilePathInEtc} = sshKeysHelper.getAuthKeysSource; + }; + + services.openssh = config.ghaf.security.sshKeys.sshAuthorizedKeysCommand; + }; +} diff --git a/modules/microvm/power-control.nix b/modules/microvm/power-control.nix new file mode 100644 index 000000000..323d01087 --- /dev/null +++ b/modules/microvm/power-control.nix @@ -0,0 +1,15 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.host.powercontrol; +in +{ + options.ghaf.host.powercontrol.enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Enable host power control"; + }; + + config = lib.mkIf cfg.enable { }; +} diff --git a/modules/microvm/virtualization/microvm/adminvm.nix b/modules/microvm/virtualization/microvm/adminvm.nix new file mode 100644 index 000000000..0395c1d04 --- /dev/null +++ b/modules/microvm/virtualization/microvm/adminvm.nix @@ -0,0 +1,134 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ inputs }: +{ config, lib, ... }: +let + configHost = config; + vmName = "admin-vm"; + macAddress = "02:00:00:AD:01:01"; + isLoggingEnabled = config.ghaf.logging.client.enable; + + adminvmBaseConfiguration = { + imports = [ + inputs.self.nixosModules.givc-adminvm + (import ./common/vm-networking.nix { + inherit + config + lib + vmName + macAddress + ; + internalIP = 10; + }) + # We need to retrieve mac address and start log aggregator + ../../../common/logging/hw-mac-retrieve.nix + ../../../common/logging/logs-aggregator.nix + ( + { lib, ... }: + { + ghaf = { + users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; + profiles.debug.enable = lib.mkDefault configHost.ghaf.profiles.debug.enable; + development = { + # NOTE: SSH port also becomes accessible on the network interface + # that has been passed through to VM + ssh.daemon.enable = lib.mkDefault configHost.ghaf.development.ssh.daemon.enable; + debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; + nix-setup.enable = lib.mkDefault configHost.ghaf.development.nix-setup.enable; + }; + systemd = { + enable = true; + withName = "adminvm-systemd"; + withAudit = configHost.ghaf.profiles.debug.enable; + withNss = true; + withResolved = true; + withPolkit = true; + withTimesyncd = true; + withDebug = configHost.ghaf.profiles.debug.enable; + withHardenedConfigs = true; + }; + + givc.adminvm.enable = true; + + # Log aggregation configuration + logging.client.enable = isLoggingEnabled; + logging.listener.address = configHost.ghaf.logging.listener.address; + logging.listener.port = configHost.ghaf.logging.listener.port; + logging.identifierFilePath = "/var/lib/private/alloy/MACAddress"; + logging.server.endpoint = "https://loki.ghaflogs.vedenemo.dev/loki/api/v1/push"; + }; + + system.stateVersion = lib.trivial.release; + + nixpkgs.buildPlatform.system = configHost.nixpkgs.buildPlatform.system; + nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system; + + networking = { + firewall.allowedTCPPorts = lib.mkIf isLoggingEnabled [ config.ghaf.logging.listener.port ]; + firewall.allowedUDPPorts = [ ]; + }; + + systemd.network = { + enable = true; + networks."10-ethint0" = { + matchConfig.MACAddress = macAddress; + linkConfig.ActivationPolicy = "always-up"; + }; + }; + + microvm = { + optimize.enable = true; + #TODO: Add back support cloud-hypervisor + #the system fails to switch root to the stage2 with cloud-hypervisor + hypervisor = "qemu"; + shares = + [ + { + tag = "ro-store"; + source = "/nix/store"; + mountPoint = "/nix/.ro-store"; + proto = "virtiofs"; + } + ] + ++ lib.optionals isLoggingEnabled [ + { + # Creating a persistent log-store which is mapped on ghaf-host + # This is only to preserve logs state across adminvm reboots + tag = "log-store"; + source = "/var/lib/private/alloy"; + mountPoint = "/var/lib/private/alloy"; + proto = "virtiofs"; + } + ]; + + writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; + }; + imports = [ ../../../common ]; + } + ) + ]; + }; + cfg = config.ghaf.virtualization.microvm.adminvm; +in +{ + options.ghaf.virtualization.microvm.adminvm = { + enable = lib.mkEnableOption "AdminVM"; + + extraModules = lib.mkOption { + description = '' + List of additional modules to be imported and evaluated as part of + AdminVM's NixOS configuration. + ''; + default = [ ]; + }; + }; + + config = lib.mkIf cfg.enable { + microvm.vms."${vmName}" = { + autostart = true; + config = adminvmBaseConfiguration // { + imports = adminvmBaseConfiguration.imports ++ cfg.extraModules; + }; + }; + }; +} diff --git a/modules/microvm/virtualization/microvm/appvm.nix b/modules/microvm/virtualization/microvm/appvm.nix index 2481666f6..fb6659a8a 100644 --- a/modules/microvm/virtualization/microvm/appvm.nix +++ b/modules/microvm/virtualization/microvm/appvm.nix @@ -1,199 +1,257 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 +{ inputs }: { config, lib, + pkgs, ... -}: let +}: +let + inherit (lib) mkOption types optional; + configHost = config; cfg = config.ghaf.virtualization.microvm.appvm; - makeVm = { - vm, - index, - }: let - vmName = "${vm.name}-vm"; - cid = - if vm.cid > 0 - then vm.cid - else cfg.vsockBaseCID + index; - appvmConfiguration = { - imports = [ - (import ./common/vm-networking.nix { - inherit vmName; - inherit (vm) macAddress; - }) - ({ - lib, - config, - pkgs, - ... - }: let - waypipeBorder = - if vm.borderColor != null - then "--border \"${vm.borderColor}\"" - else ""; - runWaypipe = with pkgs; - writeScriptBin "run-waypipe" '' - #!${runtimeShell} -e - ${pkgs.waypipe}/bin/waypipe --vsock -s ${toString configHost.ghaf.virtualization.microvm.guivm.waypipePort} ${waypipeBorder} server $@ - ''; - in { - ghaf = { - users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; - - development = { - ssh.daemon.enable = lib.mkDefault configHost.ghaf.development.ssh.daemon.enable; - debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; - nix-setup.enable = lib.mkDefault configHost.ghaf.development.nix-setup.enable; - }; - systemd = { - enable = true; - withName = "appvm-systemd"; - withNss = true; - withResolved = true; - withPolkit = true; - withDebug = configHost.ghaf.profiles.debug.enable; - }; - }; - # SSH is very picky about the file permissions and ownership and will - # accept neither direct path inside /nix/store or symlink that points - # there. Therefore we copy the file to /etc/ssh/get-auth-keys (by - # setting mode), instead of symlinking it. - environment.etc."ssh/get-auth-keys" = { - source = let - script = pkgs.writeShellScriptBin "get-auth-keys" '' - [[ "$1" != "ghaf" ]] && exit 0 - ${pkgs.coreutils}/bin/cat /run/waypipe-ssh-public-key/id_ed25519.pub + sshKeysHelper = pkgs.callPackage ../../../../packages/ssh-keys-helper { + inherit pkgs; + config = configHost; + }; + + makeVm = + { vm, index }: + let + vmName = "${vm.name}-vm"; + cid = if vm.cid > 0 then vm.cid else cfg.vsockBaseCID + index; + appvmConfiguration = { + imports = [ + inputs.impermanence.nixosModules.impermanence + inputs.self.nixosModules.givc-appvm + (import ./common/vm-networking.nix { + inherit config lib vmName; + inherit (vm) macAddress; + internalIP = index + 100; + }) + + ./common/storagevm.nix + + # To push logs to central location + ../../../common/logging/client.nix + ( + { + lib, + config, + pkgs, + ... + }: + let + waypipeBorder = if vm.borderColor != null then "--border \"${vm.borderColor}\"" else ""; + runWaypipe = pkgs.writeScriptBin "run-waypipe" '' + #!${pkgs.runtimeShell} -e + ${pkgs.waypipe}/bin/waypipe --vsock -s ${toString configHost.ghaf.virtualization.microvm.guivm.waypipePort} ${waypipeBorder} server "$@" ''; - in "${script}/bin/get-auth-keys"; - mode = "0555"; - }; - services.openssh = { - authorizedKeysCommand = "/etc/ssh/get-auth-keys"; - authorizedKeysCommandUser = "nobody"; - }; + in + { + ghaf = { + users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; + profiles.debug.enable = lib.mkDefault configHost.ghaf.profiles.debug.enable; - system.stateVersion = lib.trivial.release; - - nixpkgs.buildPlatform.system = configHost.nixpkgs.buildPlatform.system; - nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system; - - environment.systemPackages = [ - pkgs.waypipe - runWaypipe - ]; - - microvm = { - optimize.enable = false; - mem = vm.ramMb; - vcpu = vm.cores; - hypervisor = "qemu"; - shares = [ - { - tag = "waypipe-ssh-public-key"; - source = "/run/waypipe-ssh-public-key"; - mountPoint = "/run/waypipe-ssh-public-key"; - } - { - tag = "ro-store"; - source = "/nix/store"; - mountPoint = "/nix/.ro-store"; - } - ]; - writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; + development = { + ssh.daemon.enable = lib.mkDefault configHost.ghaf.development.ssh.daemon.enable; + debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; + nix-setup.enable = lib.mkDefault configHost.ghaf.development.nix-setup.enable; + }; + systemd = { + enable = true; + withName = "appvm-systemd"; + withAudit = configHost.ghaf.profiles.debug.enable; + withNss = true; + withResolved = true; + withTimesyncd = true; + withPolkit = true; + withDebug = configHost.ghaf.profiles.debug.enable; + withHardenedConfigs = true; + }; + # Logging client configuration + logging.client.enable = configHost.ghaf.logging.client.enable; + logging.client.endpoint = configHost.ghaf.logging.client.endpoint; + }; - qemu.extraArgs = [ - "-M" - "q35,accel=kvm:tcg,mem-merge=on,sata=off" - "-device" - "vhost-vsock-pci,guest-cid=${toString cid}" - ]; - }; - fileSystems."/run/waypipe-ssh-public-key".options = ["ro"]; + # SSH is very picky about the file permissions and ownership and will + # accept neither direct path inside /nix/store or symlink that points + # there. Therefore we copy the file to /etc/ssh/get-auth-keys (by + # setting mode), instead of symlinking it. + environment.etc.${configHost.ghaf.security.sshKeys.getAuthKeysFilePathInEtc} = + sshKeysHelper.getAuthKeysSource; + services.openssh = configHost.ghaf.security.sshKeys.sshAuthorizedKeysCommand; + + system.stateVersion = lib.trivial.release; + + nixpkgs.buildPlatform.system = configHost.nixpkgs.buildPlatform.system; + nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system; + + environment.systemPackages = [ + pkgs.waypipe + runWaypipe + pkgs.tpm2-tools + pkgs.opensc + ]; + + security.tpm2 = { + enable = true; + abrmd.enable = true; + }; + + security.pki.certificateFiles = + lib.mkIf configHost.ghaf.virtualization.microvm.idsvm.mitmproxy.enable + [ ./idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca-cert.pem ]; + + microvm = { + optimize.enable = false; + mem = vm.ramMb; + vcpu = vm.cores; + hypervisor = "qemu"; + shares = [ + { + tag = "waypipe-ssh-public-key"; + source = configHost.ghaf.security.sshKeys.waypipeSshPublicKeyDir; + mountPoint = configHost.ghaf.security.sshKeys.waypipeSshPublicKeyDir; + } + { + tag = "ro-store"; + source = "/nix/store"; + mountPoint = "/nix/.ro-store"; + } + ]; + writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; - imports = [../../../common]; - }) - ]; + qemu = { + extraArgs = + [ + "-M" + "accel=kvm:tcg,mem-merge=on,sata=off" + "-device" + "vhost-vsock-pci,guest-cid=${toString cid}" + ] + ++ lib.optionals vm.vtpm.enable [ + "-chardev" + "socket,id=chrtpm,path=/var/lib/swtpm/${vm.name}-sock" + "-tpmdev" + "emulator,id=tpm0,chardev=chrtpm" + "-device" + "tpm-tis,tpmdev=tpm0" + ]; + + machine = + { + # Use the same machine type as the host + x86_64-linux = "q35"; + aarch64-linux = "virt"; + } + .${configHost.nixpkgs.hostPlatform.system}; + }; + }; + fileSystems."${configHost.ghaf.security.sshKeys.waypipeSshPublicKeyDir}".options = [ "ro" ]; + + imports = [ ../../../common ]; + } + ) + ]; + }; + in + { + autostart = true; + config = appvmConfiguration // { + imports = + appvmConfiguration.imports + ++ cfg.extraModules + ++ vm.extraModules + ++ [ { environment.systemPackages = vm.packages; } ]; + }; }; - in { - autostart = true; - config = appvmConfiguration // {imports = appvmConfiguration.imports ++ cfg.extraModules ++ vm.extraModules ++ [{environment.systemPackages = vm.packages;}];}; - }; -in { - options.ghaf.virtualization.microvm.appvm = with lib; { + + # Host service dependencies + after = optional config.ghaf.services.audio.enable "pulseaudio.service"; + requires = after; + # Sleep appvms to give gui-vm time to start + serviceConfig.ExecStartPre = "/bin/sh -c 'sleep 8'"; +in +{ + options.ghaf.virtualization.microvm.appvm = { enable = lib.mkEnableOption "appvm"; - vms = with types; - mkOption { - description = '' - List of AppVMs to be created - ''; - type = lib.types.listOf (submodule { + vms = mkOption { + description = '' + List of AppVMs to be created + ''; + type = lib.types.listOf ( + types.submodule { options = { name = mkOption { description = '' Name of the AppVM ''; - type = str; + type = types.str; }; packages = mkOption { description = '' Packages that are included into the AppVM ''; - type = types.listOf package; - default = []; + type = types.listOf types.package; + default = [ ]; }; macAddress = mkOption { description = '' AppVM's network interface MAC address ''; - type = str; + type = types.str; }; ramMb = mkOption { description = '' Amount of RAM for this AppVM ''; - type = int; + type = types.int; }; cores = mkOption { description = '' Amount of processor cores for this AppVM ''; - type = int; + type = types.int; }; extraModules = mkOption { description = '' List of additional modules to be imported and evaluated as part of appvm's NixOS configuration. ''; - default = []; + default = [ ]; }; cid = mkOption { description = '' VSOCK context identifier (CID) for the AppVM Default value 0 means auto-assign using vsockBaseCID and AppVM index ''; - type = int; + type = types.int; default = 0; }; borderColor = mkOption { description = '' Border color of the AppVM window ''; - type = nullOr str; + type = types.nullOr types.str; default = null; }; + vtpm.enable = lib.mkEnableOption "vTPM support in the virtual machine"; }; - }); - default = []; - }; + } + ); + default = [ ]; + }; extraModules = mkOption { description = '' List of additional modules to be imported and evaluated as part of appvm's NixOS configuration. ''; - default = []; + default = [ ]; }; # Base VSOCK CID which is used for auto assigning CIDs for all AppVMs @@ -208,10 +266,59 @@ in { }; }; - config = lib.mkIf cfg.enable { - microvm.vms = let - vms = lib.imap0 (index: vm: {"${vm.name}-vm" = makeVm {inherit index vm;};}) cfg.vms; + config = + let + makeSwtpmService = + { vm }: + let + swtpmScript = pkgs.writeShellApplication { + name = "${vm.name}-swtpm"; + runtimeInputs = with pkgs; [ + coreutils + swtpm + ]; + text = '' + mkdir -p /var/lib/swtpm/${vm.name}-state + swtpm socket --tpmstate dir=/var/lib/swtpm/${vm.name}-state \ + --ctrl type=unixio,path=/var/lib/swtpm/${vm.name}-sock \ + --tpm2 \ + --log level=20 + ''; + }; + in + lib.mkIf vm.vtpm.enable { + enable = true; + description = "swtpm service for ${vm.name}"; + path = [ swtpmScript ]; + wantedBy = [ "microvms.target" ]; + serviceConfig = { + Type = "simple"; + User = "microvm"; + Restart = "always"; + StateDirectory = "swtpm"; + StandardOutput = "journal"; + StandardError = "journal"; + ExecStart = "${swtpmScript}/bin/${vm.name}-swtpm"; + }; + }; in - lib.foldr lib.recursiveUpdate {} vms; - }; + lib.mkIf cfg.enable { + microvm.vms = + let + vms = lib.imap0 (index: vm: { "${vm.name}-vm" = makeVm { inherit index vm; }; }) cfg.vms; + in + lib.foldr lib.recursiveUpdate { } vms; + + # Apply host service dependencies, add swtpm + systemd.services = + let + serviceDependencies = map (vm: { + "microvm@${vm.name}-vm" = { + inherit after requires serviceConfig; + }; + "${vm.name}-swtpm" = makeSwtpmService { inherit vm; }; + }) cfg.vms; + in + lib.foldr lib.recursiveUpdate { } serviceDependencies; + }; } diff --git a/modules/microvm/virtualization/microvm/audiovm.nix b/modules/microvm/virtualization/microvm/audiovm.nix new file mode 100644 index 000000000..7d90ee030 --- /dev/null +++ b/modules/microvm/virtualization/microvm/audiovm.nix @@ -0,0 +1,164 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ inputs }: +{ + config, + lib, + pkgs, + ... +}: +let + configHost = config; + vmName = "audio-vm"; + macAddress = "02:00:00:03:03:03"; + isGuiVmEnabled = config.ghaf.virtualization.microvm.guivm.enable; + + sshKeysHelper = pkgs.callPackage ../../../../packages/ssh-keys-helper { + inherit pkgs; + inherit config; + }; + + audiovmBaseConfiguration = { + imports = [ + inputs.self.nixosModules.givc-audiovm + (import ./common/vm-networking.nix { + inherit + config + lib + vmName + macAddress + ; + internalIP = 5; + }) + ( + { lib, pkgs, ... }: + { + imports = [ ../../../common ]; + + ghaf = { + users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; + profiles.debug.enable = lib.mkDefault configHost.ghaf.profiles.debug.enable; + + development = { + ssh.daemon.enable = lib.mkDefault configHost.ghaf.development.ssh.daemon.enable; + debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; + nix-setup.enable = lib.mkDefault configHost.ghaf.development.nix-setup.enable; + }; + systemd = { + enable = true; + withName = "audiovm-systemd"; + withAudit = configHost.ghaf.profiles.debug.enable; + withNss = true; + withResolved = true; + withTimesyncd = true; + withDebug = configHost.ghaf.profiles.debug.enable; + }; + givc.audiovm.enable = true; + services.audio.enable = true; + }; + + environment = { + systemPackages = [ + pkgs.pulseaudio + pkgs.pamixer + pkgs.pipewire + ] ++ lib.optional config.ghaf.development.debug.tools.enable pkgs.alsa-utils; + }; + + time.timeZone = config.time.timeZone; + system.stateVersion = lib.trivial.release; + + nixpkgs = { + buildPlatform.system = configHost.nixpkgs.buildPlatform.system; + hostPlatform.system = configHost.nixpkgs.hostPlatform.system; + }; + + services.openssh = config.ghaf.security.sshKeys.sshAuthorizedKeysCommand; + + microvm = { + # Optimize is disabled because when it is enabled, qemu is built without libusb + optimize.enable = false; + vcpu = 1; + mem = 256; + hypervisor = "qemu"; + shares = + [ + { + tag = "ro-store"; + source = "/nix/store"; + mountPoint = "/nix/.ro-store"; + } + ] + ++ lib.optionals isGuiVmEnabled [ + { + # Add the waypipe-ssh public key to the microvm + tag = config.ghaf.security.sshKeys.waypipeSshPublicKeyName; + source = config.ghaf.security.sshKeys.waypipeSshPublicKeyDir; + mountPoint = config.ghaf.security.sshKeys.waypipeSshPublicKeyDir; + } + ]; + writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; + qemu = { + machine = + { + # Use the same machine type as the host + x86_64-linux = "q35"; + aarch64-linux = "virt"; + } + .${configHost.nixpkgs.hostPlatform.system}; + extraArgs = [ + "-device" + "qemu-xhci" + ]; + }; + }; + + fileSystems = lib.mkIf isGuiVmEnabled { + ${config.ghaf.security.sshKeys.waypipeSshPublicKeyDir}.options = [ "ro" ]; + }; + + # Fixed IP-address for debugging subnet + # SSH is very picky about to file permissions and ownership and will + # accept neither direct path inside /nix/store or symlink that points + # there. Therefore we copy the file to /etc/ssh/get-auth-keys (by + # setting mode), instead of symlinking it. + environment.etc = lib.mkIf isGuiVmEnabled { + ${config.ghaf.security.sshKeys.getAuthKeysFilePathInEtc} = sshKeysHelper.getAuthKeysSource; + }; + + systemd.network.networks."10-ethint0".addresses = + let + getAudioVmEntry = builtins.filter ( + x: x.name == "audio-vm" + lib.optionalString config.ghaf.profiles.debug.enable "-debug" + ) config.ghaf.networking.hosts.entries; + ip = lib.head (builtins.map (x: x.ip) getAudioVmEntry); + in + [ { Address = "${ip}/24"; } ]; + } + ) + ]; + }; + cfg = config.ghaf.virtualization.microvm.audiovm; +in +{ + options.ghaf.virtualization.microvm.audiovm = { + enable = lib.mkEnableOption "AudioVM"; + + extraModules = lib.mkOption { + description = '' + List of additional modules to be imported and evaluated as part of + AudioVM's NixOS configuration. + ''; + default = [ ]; + }; + }; + + config = lib.mkIf cfg.enable { + microvm.vms."${vmName}" = { + autostart = true; + config = audiovmBaseConfiguration // { + imports = audiovmBaseConfiguration.imports ++ cfg.extraModules; + }; + }; + }; +} diff --git a/modules/microvm/virtualization/microvm/common/storagevm.nix b/modules/microvm/virtualization/microvm/common/storagevm.nix new file mode 100644 index 000000000..6f86423b0 --- /dev/null +++ b/modules/microvm/virtualization/microvm/common/storagevm.nix @@ -0,0 +1,88 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ lib, config, ... }: +let + cfg = config.ghaf.storagevm; + mountPath = "/tmp/storagevm"; +in +{ + options.ghaf.storagevm = with lib; { + enable = mkEnableOption "StorageVM support"; + + name = mkOption { + description = '' + Name of the corresponding directory on the storage virtual machine. + ''; + type = types.str; + }; + + directories = mkOption { + # FIXME: Probably will lead to disgraceful error messages, as we + # put typechecking on nix impermanence option. But other, + # proper, ways are much harder. + type = types.anything; + default = [ ]; + example = [ + "Downloads" + "Music" + "Pictures" + "Documents" + "Videos" + ]; + description = '' + Directories to bind mount to persistent storage. + ''; + }; + + users = mkOption { + type = types.anything; + default = { }; + example = { + "user".directories = [ + "Downloads" + "Music" + "Pictures" + "Documents" + "Videos" + ]; + }; + description = '' + User-specific directories to bind mount to persistent storage. + ''; + }; + + files = mkOption { + type = types.anything; + default = [ ]; + example = [ "/etc/machine-id" ]; + description = '' + Files to bind mount to persistent storage. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + fileSystems.${mountPath}.neededForBoot = true; + + microvm.shares = [ + { + tag = "hostshare"; + proto = "virtiofs"; + securityModel = "passthrough"; + source = "/storagevm/${cfg.name}"; + mountPoint = mountPath; + } + ]; + + environment.persistence.${mountPath} = lib.mkMerge [ + { + hideMounts = true; + files = [ + "/etc/ssh/ssh_host_ed25519_key.pub" + "/etc/ssh/ssh_host_ed25519_key" + ]; + } + { inherit (cfg) directories users files; } + ]; + }; +} diff --git a/modules/microvm/virtualization/microvm/common/vm-networking.nix b/modules/microvm/virtualization/microvm/common/vm-networking.nix index 5f977355c..6d9dc1886 100644 --- a/modules/microvm/virtualization/microvm/common/vm-networking.nix +++ b/modules/microvm/virtualization/microvm/common/vm-networking.nix @@ -1,21 +1,33 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 { + config, + lib, vmName, macAddress, + internalIP, + isGateway ? false, ... -}: let +}: +let networkName = "ethint0"; -in { + netVmEntry = builtins.filter (x: x.name == "net-vm") config.ghaf.networking.hosts.entries; + netVmAddress = builtins.map (x: x.ip) netVmEntry; + isIdsvmEnabled = config.ghaf.virtualization.microvm.idsvm.enable; + idsVmEntry = builtins.filter (x: x.name == "ids-vm") config.ghaf.networking.hosts.entries; + idsVmAddress = lib.optionals isIdsvmEnabled (builtins.map (x: x.ip) idsVmEntry); + gateway = if isIdsvmEnabled && (vmName != "ids-vm") then idsVmAddress else netVmAddress; +in +{ networking = { hostName = vmName; enableIPv6 = false; - firewall.allowedTCPPorts = [22]; - firewall.allowedUDPPorts = [67]; + firewall.allowedTCPPorts = [ 22 ]; + firewall.allowedUDPPorts = [ 67 ]; useNetworkd = true; nat = { enable = true; - internalInterfaces = [networkName]; + internalInterfaces = [ networkName ]; }; }; @@ -37,10 +49,17 @@ in { }; networks."10-${networkName}" = { matchConfig.MACAddress = macAddress; - DHCP = "yes"; + addresses = + [ { Address = "192.168.100.${toString internalIP}/24"; } ] + ++ lib.optionals config.ghaf.profiles.debug.enable [ + { + # IP-address for debugging subnet + Address = "192.168.101.${toString internalIP}/24"; + } + ]; linkConfig.RequiredForOnline = "routable"; linkConfig.ActivationPolicy = "always-up"; - }; + } // lib.optionalAttrs (!isGateway) { inherit gateway; }; }; # systemd-resolved does not support local names resolution diff --git a/modules/microvm/virtualization/microvm/qemu-gpio-guestvm.dtb b/modules/microvm/virtualization/microvm/dtb/qemu-gpio-guestvm.dtb similarity index 100% rename from modules/microvm/virtualization/microvm/qemu-gpio-guestvm.dtb rename to modules/microvm/virtualization/microvm/dtb/qemu-gpio-guestvm.dtb diff --git a/modules/jetpack-microvm/qemu-gpio-guestvm.dts b/modules/microvm/virtualization/microvm/dtb/qemu-gpio-guestvm.dts similarity index 100% rename from modules/jetpack-microvm/qemu-gpio-guestvm.dts rename to modules/microvm/virtualization/microvm/dtb/qemu-gpio-guestvm.dts diff --git a/modules/microvm/virtualization/microvm/gpiovm.nix b/modules/microvm/virtualization/microvm/gpiovm.nix index eee981865..9a8d37b41 100644 --- a/modules/microvm/virtualization/microvm/gpiovm.nix +++ b/modules/microvm/virtualization/microvm/gpiovm.nix @@ -1,100 +1,210 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 +{ inputs }: { config, lib, pkgs, ... -} : with pkgs; let +}: with pkgs; +let configHost = config; vmName = "gpio-vm"; + macAddress = "03:00:00:07:06:05"; - gpioGuestDtbName = ./qemu-gpio-guestvm.dtb; - tmp_rootfs = ./tegra_rootfs.qcow2; + isGuiVmEnabled = config.ghaf.virtualization.microvm.guivm.enable; + + sshKeysHelper = pkgs.callPackage ../../../../packages/ssh-keys-helper { + inherit pkgs; + inherit config; + }; + + kernelPath = "${config.system.build.kernel}"; + guestKernel = "${kernelPath}/Image"; # the host's kernel image can be used in the guest + + dtsName = "qemu-gpio-guestvm.dts"; + dtbName = "qemu-gpio-guestvm.dtb"; + + # Build the guest specific DTB file for GPIO passthrough + gpioDtbDerivation = builtins.trace "Creating guest DTB" pkgs.stdenv.mkDerivation { + pname = "gpio-vm-dtb"; + version = "1.0"; + + src = ./dtb; + buildInputs = [ pkgs.dtc ]; + + # unpackPhase = '' + # mkdir -p ${kernelPath}/dtbs + # cp ${dtsName} ${kernelPath}/dtbs/ + # ''; + + buildPhase = '' + mkdir -p $out + # ls -thog $src + dtc -I dts -O dtb -o $out/${dtbName} $src/${dtsName} + # ls -thog $out + ''; + + installPhase = '' + # cp $src/${dtsName} ${kernelPath}/dtbs/ + # cp $out/${dtbName} ${kernelPath}/dtbs/ + ''; + + outputs = [ "out" ]; + }; + + gpioGuestDtb = "${gpioDtbDerivation}/${dtbName}"; gpiovmBaseConfiguration = { imports = [ - ({lib, ...}: { - ghaf = { - users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; - /* - development = { - debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; - nix-setup.enable = lib.mkDefault configHost.ghaf.development.nix-setup.enable; + inputs.impermanence.nixosModules.impermanence + inputs.self.nixosModules.givc-gpiovm + (import ./common/vm-networking.nix { + inherit + config + lib + vmName + macAddress + ; + internalIP = 1; + isGateway = true; + }) + + ./common/storagevm.nix + + # To push logs to central location + ../../../common/logging/client.nix + ( + { lib, ... }: + { + imports = [ ../../../common ]; + ghaf = { + users.accounts.enable = lib.mkDefault config.ghaf.users.accounts.enable; + profiles.debug.enable = lib.mkDefault config.ghaf.profiles.debug.enable; + development = { + # NOTE: SSH port also becomes accessible on the network interface + # that has been passed through to gpiovm + ssh.daemon.enable = lib.mkDefault config.ghaf.development.ssh.daemon.enable; + debug.tools.enable = lib.mkDefault config.ghaf.development.debug.tools.enable; + nix-setup.enable = lib.mkDefault config.ghaf.development.nix-setup.enable; + }; + systemd = { + enable = true; + withName = "gpiovm-systemd"; + # withAudit = config.ghaf.profiles.debug.enable; + # withPolkit = true; + # withResolved = true; + # withTimesyncd = true; + # withDebug = config.ghaf.profiles.debug.enable; + # withHardenedConfigs = true; + }; + givc.gpiovm.enable = true; + # Logging client configuration + logging.client.enable = config.ghaf.logging.client.enable; + logging.client.endpoint = config.ghaf.logging.client.endpoint; + storagevm = { + enable = true; + name = "gpiovm"; + directories = [ "/etc/NetworkManager/system-connections/" ]; + }; }; - */ - systemd = { - enable = true; - withName = "gpiovm-systemd"; - withPolkit = true; - # withDebug = configHost.ghaf.profiles.debug.enable; + + time.timeZone = config.time.timeZone; + system.stateVersion = lib.trivial.release; + + nixpkgs = { + buildPlatform.system = config.nixpkgs.buildPlatform.system; + hostPlatform.system = config.nixpkgs.hostPlatform.system; }; - }; - system.stateVersion = lib.trivial.release; + networking = { + firewall.allowedTCPPorts = [ 53 ]; + firewall.allowedUDPPorts = [ 53 ]; + }; - nixpkgs.buildPlatform.system = configHost.nixpkgs.buildPlatform.system; - nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system; + services.openssh = config.ghaf.security.sshKeys.sshAuthorizedKeysCommand; + # WORKAROUND: Create a rule to temporary hardcode device name for Wi-Fi adapter on x86 + # TODO this is a dirty hack to guard against adding this to Nvidia/vm targets which + /* + # dont have that definition structure yet defined. FIXME. + # TODO the hardware.definition should not even be exposed in targets that do not consume it + services.udev.extraRules = lib.mkIf (config.ghaf.hardware.definition.network.pciDevices != [ ]) '' + SUBSYSTEM=="net", ACTION=="add", ATTRS{vendor}=="0x${(lib.head config.ghaf.hardware.definition.network.pciDevices).vendorId}", ATTRS{device}=="0x${(lib.head config.ghaf.hardware.definition.network.pciDevices).productId}", NAME="${(lib.head config.ghaf.hardware.definition.network.pciDevices).name}" + ''; + */ + microvm = { + optimize.enable = true; + hypervisor = "qemu"; + shares = + [ + { + tag = "ro-store"; + source = "/nix/store"; + mountPoint = "/nix/.ro-store"; + } + ] + ++ lib.optionals isGuiVmEnabled [ + { + # Add the waypipe-ssh public key to the microvm + tag = config.ghaf.security.sshKeys.waypipeSshPublicKeyName; + source = config.ghaf.security.sshKeys.waypipeSshPublicKeyDir; + mountPoint = config.ghaf.security.sshKeys.waypipeSshPublicKeyDir; + } + ]; - /* - services.xxx = { - # we define a servce in extraModules variable below with import ./gpio-test.nix - } - */ - microvm = { - optimize.enable = true; - hypervisor = "qemu"; - - shares = [ - { - tag = "ro-store"; - source = "/nix/store"; - mountPoint = "/nix/.ro-store"; - } - ]; - # writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; - - graphics.enable= false; - qemu = { - /* tmp removed for GPIO testing - machine = - { - # Use the same machine type as the host - x86_64-linux = "q35"; - aarch64-linux ="virt"; - } - .${configHost.nixpkgs.hostPlatform.system}; - */ - serialConsole = true; - extraArgs = builtins.trace "GpioVM: Evaluating QEMU parameters for gpio-vm" [ - "-dtb" "${gpioGuestDtbName}" - # "-serial" "/dev/tty10" # Could not open '/dev/tty10': Permission denied - ]; - /* - extraArgs = builtins.trace "GpioVM: Evaluating qemu.extraArgs for gpio-vm" [ - # Add custom dtb to Gpio-VM with VDA - "-dtb ${gpioGuestDtbName}" - "-monitor chardev=mon0,mode=readline" - "-no-reboot" - # "-drive file=${tmp_rootfs},if=virtio,format=qcow2" - # -nographic \ - # -machine virt,accel=kvm \q - # -cpu host \ - # -m 4G \ - # -smp 2 \ - # -kernel ${kernel} \ - # "-monitor" "chardev=ttyTHS2,mode=readline" + writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; + + kernelParams = [ + "rootwait root=/dev/vda" ]; - */ + graphics.enable = false; + qemu = { + # qemu = builtins.trace "Qemu params, filenames: ${dtsName}, ${dtbName}, ${guestKernel}, ${guestRootFsName}" { + machine = + { + # Use the same machine type as the host + x86_64-linux = "q35"; + aarch64-linux = "virt"; + } + .${config.nixpkgs.hostPlatform.system}; + serialConsole = true; + extraArgs = lib.mkForce [ + # extraArgs = builtins.trace "GpioVM: Evaluating qemu.extraArgs for gpio-vm" [ + "-sandbox" "on" + "-nographic" + "-no-reboot" + "-dtb" "${gpioGuestDtb}" + "-kernel" "${guestKernel}" + # "-drive" "file=${guestRootFs},if=virtio,format=qcow2" + "-machine" "virt,accel=kvm" + "-cpu" "host" + "-m" "2G" + "-smp" "2" + "-serial" "pty" + # "-net" "user,hostfwd=tcp::2222-:22" + # "-net" "nic" + ]; + }; }; - }; - imports = [../../../common]; - }) + fileSystems = lib.mkIf isGuiVmEnabled { + ${config.ghaf.security.sshKeys.waypipeSshPublicKeyDir}.options = [ "ro" ]; + }; + + # SSH is very picky about to file permissions and ownership and will + # accept neither direct path inside /nix/store or symlink that points + # there. Therefore we copy the file to /etc/ssh/get-auth-keys (by + # setting mode), instead of symlinking it. + environment.etc = lib.mkIf isGuiVmEnabled { + ${config.ghaf.security.sshKeys.getAuthKeysFilePathInEtc} = sshKeysHelper.getAuthKeysSource; + }; + } + ) ]; }; cfg = config.ghaf.virtualization.microvm.gpiovm; -in { +in +{ options.ghaf.virtualization.microvm.gpiovm = { enable = lib.mkEnableOption "GpioVM"; diff --git a/modules/microvm/virtualization/microvm/guivm.nix b/modules/microvm/virtualization/microvm/guivm.nix index b8200e874..c86ff6222 100644 --- a/modules/microvm/virtualization/microvm/guivm.nix +++ b/modules/microvm/virtualization/microvm/guivm.nix @@ -1,142 +1,197 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 +{ inputs }: { config, lib, pkgs, ... -}: let - configHost = config; +}: +let vmName = "gui-vm"; macAddress = "02:00:00:02:02:02"; + inherit (import ../../../../lib/launcher.nix { inherit pkgs lib; }) rmDesktopEntries; guivmBaseConfiguration = { imports = [ - (import ./common/vm-networking.nix {inherit vmName macAddress;}) - ({ - lib, - pkgs, - ... - }: { - ghaf = { - users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; - profiles.debug.enable = lib.mkDefault configHost.ghaf.profiles.debug.enable; - profiles.graphics.enable = true; - # To enable screen locking set graphics.labwc.lock to true - graphics.labwc.lock.enable = false; - profiles.applications.enable = false; - windows-launcher.enable = false; - development = { - ssh.daemon.enable = lib.mkDefault configHost.ghaf.development.ssh.daemon.enable; - debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; - nix-setup.enable = lib.mkDefault configHost.ghaf.development.nix-setup.enable; + inputs.impermanence.nixosModules.impermanence + inputs.self.nixosModules.givc-guivm + (import ./common/vm-networking.nix { + inherit + config + lib + vmName + macAddress + ; + internalIP = 3; + }) + + ./common/storagevm.nix + + # To push logs to central location + ../../../common/logging/client.nix + ( + { lib, pkgs, ... }: + { + ghaf = { + users.accounts.enable = lib.mkDefault config.ghaf.users.accounts.enable; + profiles = { + debug.enable = lib.mkDefault config.ghaf.profiles.debug.enable; + applications.enable = false; + graphics.enable = true; + }; + # To enable screen locking set to true + graphics.labwc.autolock.enable = false; + development = { + ssh.daemon.enable = lib.mkDefault config.ghaf.development.ssh.daemon.enable; + debug.tools.enable = lib.mkDefault config.ghaf.development.debug.tools.enable; + nix-setup.enable = lib.mkDefault config.ghaf.development.nix-setup.enable; + }; + systemd = { + enable = true; + withName = "guivm-systemd"; + withAudit = config.ghaf.profiles.debug.enable; + withNss = true; + withResolved = true; + withTimesyncd = true; + withDebug = config.ghaf.profiles.debug.enable; + withHardenedConfigs = true; + }; + givc.guivm.enable = true; + # Logging client configuration + logging.client.enable = config.ghaf.logging.client.enable; + logging.client.endpoint = config.ghaf.logging.client.endpoint; + storagevm = { + enable = true; + name = "guivm"; + directories = [ + { + directory = "/home/${config.ghaf.users.accounts.user}/"; + inherit (config.ghaf.users.accounts) user; + group = config.ghaf.users.accounts.user; + mode = "u=rwx,g=,o="; + } + ]; + }; }; - systemd = { - enable = true; - withName = "guivm-systemd"; - withNss = true; - withResolved = true; - withTimesyncd = true; - withDebug = configHost.ghaf.profiles.debug.enable; + + systemd.services."waypipe-ssh-keygen" = + let + keygenScript = pkgs.writeShellScriptBin "waypipe-ssh-keygen" '' + set -xeuo pipefail + mkdir -p /run/waypipe-ssh + echo -en "\n\n\n" | ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f /run/waypipe-ssh/id_ed25519 -C "" + chown ghaf:ghaf /run/waypipe-ssh/* + cp /run/waypipe-ssh/id_ed25519.pub /run/waypipe-ssh-public-key/id_ed25519.pub + ''; + in + { + enable = true; + description = "Generate SSH keys for Waypipe"; + path = [ keygenScript ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + StandardOutput = "journal"; + StandardError = "journal"; + ExecStart = "${keygenScript}/bin/waypipe-ssh-keygen"; + }; + }; + + environment = { + systemPackages = + (rmDesktopEntries [ + pkgs.waypipe + pkgs.networkmanagerapplet + ]) + ++ [ + pkgs.nm-launcher + pkgs.pamixer + ] + ++ (lib.optional ( + config.ghaf.profiles.debug.enable && config.ghaf.virtualization.microvm.idsvm.mitmproxy.enable + ) pkgs.mitmweb-ui); }; - }; - systemd.services."waypipe-ssh-keygen" = let - keygenScript = pkgs.writeShellScriptBin "waypipe-ssh-keygen" '' - set -xeuo pipefail - mkdir -p /run/waypipe-ssh - echo -en "\n\n\n" | ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f /run/waypipe-ssh/id_ed25519 -C "" - chown ghaf:ghaf /run/waypipe-ssh/* - cp /run/waypipe-ssh/id_ed25519.pub /run/waypipe-ssh-public-key/id_ed25519.pub - ''; - in { - enable = true; - description = "Generate SSH keys for Waypipe"; - path = [keygenScript]; - wantedBy = ["multi-user.target"]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - StandardOutput = "journal"; - StandardError = "journal"; - ExecStart = "${keygenScript}/bin/waypipe-ssh-keygen"; + time.timeZone = config.time.timeZone; + system.stateVersion = lib.trivial.release; + + nixpkgs = { + buildPlatform.system = config.nixpkgs.buildPlatform.system; + hostPlatform.system = config.nixpkgs.hostPlatform.system; }; - }; - environment = { - systemPackages = [ - pkgs.waypipe - pkgs.networkmanagerapplet - pkgs.nm-launcher - ]; - }; + # Suspend inside Qemu causes segfault + # See: https://gitlab.com/qemu-project/qemu/-/issues/2321 + services.logind.lidSwitch = "ignore"; - system.stateVersion = lib.trivial.release; + microvm = { + optimize.enable = false; + vcpu = 2; + mem = 2048; + hypervisor = "qemu"; + shares = [ + { + tag = "rw-waypipe-ssh-public-key"; + source = config.ghaf.security.sshKeys.waypipeSshPublicKeyDir; + mountPoint = config.ghaf.security.sshKeys.waypipeSshPublicKeyDir; + } + { + tag = "ro-store"; + source = "/nix/store"; + mountPoint = "/nix/.ro-store"; + } + ]; + writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; - nixpkgs.buildPlatform.system = configHost.nixpkgs.buildPlatform.system; - nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system; + qemu = { + extraArgs = [ + "-device" + "vhost-vsock-pci,guest-cid=${toString cfg.vsockCID}" + ]; - microvm = { - optimize.enable = false; - vcpu = 2; - mem = 2048; - hypervisor = "qemu"; - shares = [ - { - tag = "rw-waypipe-ssh-public-key"; - source = "/run/waypipe-ssh-public-key"; - mountPoint = "/run/waypipe-ssh-public-key"; - } - { - tag = "ro-store"; - source = "/nix/store"; - mountPoint = "/nix/.ro-store"; - } - ]; - writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; + machine = + { + # Use the same machine type as the host + x86_64-linux = "q35"; + aarch64-linux = "virt"; + } + .${config.nixpkgs.hostPlatform.system}; + }; + }; - qemu.extraArgs = [ - "-device" - "vhost-vsock-pci,guest-cid=${toString cfg.vsockCID}" + imports = [ + ../../../common + ../../../desktop ]; - }; - imports = [ - ../../../common - ../../../desktop - ]; - - # Waypipe service runs in the GUIVM and listens for incoming connections from AppVMs - systemd.user.services.waypipe = { - enable = true; - description = "waypipe"; - after = ["weston.service" "labwc.service"]; - serviceConfig = { - Type = "simple"; - ExecStart = "${pkgs.waypipe}/bin/waypipe --vsock -s ${toString cfg.waypipePort} client"; - Restart = "always"; - RestartSec = "1"; + # Waypipe service runs in the GUIVM and listens for incoming connections from AppVMs + systemd.user.services.waypipe = { + enable = true; + description = "waypipe"; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.waypipe}/bin/waypipe --vsock -s ${toString cfg.waypipePort} client"; + Restart = "always"; + RestartSec = "1"; + }; + startLimitIntervalSec = 0; + partOf = [ "ghaf-session.target" ]; + wantedBy = [ "ghaf-session.target" ]; }; - startLimitIntervalSec = 0; - wantedBy = ["ghaf-session.target"]; - }; - - # Fixed IP-address for debugging subnet - systemd.network.networks."10-ethint0".addresses = [ - { - addressConfig.Address = "192.168.101.3/24"; - } - ]; - }) + } + ) ]; }; cfg = config.ghaf.virtualization.microvm.guivm; - vsockproxy = pkgs.callPackage ../../../../packages/vsockproxy {}; + vsockproxy = pkgs.callPackage ../../../../packages/vsockproxy { }; # Importing kernel builder function and building guest_graphics_hardened_kernel - buildKernel = import ../../../packages/kernel {inherit config pkgs lib;}; - config_baseline = ../../hardware/x86_64-generic/kernel/configs/ghaf_host_hardened_baseline-x86; - guest_graphics_hardened_kernel = buildKernel {inherit config_baseline;}; -in { + buildKernel = import ../../../../packages/kernel { inherit config pkgs lib; }; + config_baseline = ../../../hardware/x86_64-generic/kernel/configs/ghaf_host_hardened_baseline-x86; + guest_graphics_hardened_kernel = buildKernel { inherit config_baseline; }; +in +{ options.ghaf.virtualization.microvm.guivm = { enable = lib.mkEnableOption "GUIVM"; @@ -145,7 +200,7 @@ in { List of additional modules to be imported and evaluated as part of GUIVM's NixOS configuration. ''; - default = []; + default = [ ]; }; # GUIVM uses a VSOCK which requires a CID @@ -174,38 +229,36 @@ in { config = lib.mkIf cfg.enable { microvm.vms."${vmName}" = { autostart = true; - config = - guivmBaseConfiguration - // { - boot.kernelPackages = - lib.mkIf config.ghaf.guest.kernel.hardening.graphics.enable - (pkgs.linuxPackagesFor guest_graphics_hardened_kernel); - - imports = - guivmBaseConfiguration.imports - ++ cfg.extraModules; - }; + config = guivmBaseConfiguration // { + boot.kernelPackages = lib.mkIf config.ghaf.guest.kernel.hardening.graphics.enable ( + pkgs.linuxPackagesFor guest_graphics_hardened_kernel + ); + + imports = guivmBaseConfiguration.imports ++ cfg.extraModules; + }; }; # This directory needs to be created before any of the microvms start. - systemd.services."create-waypipe-ssh-public-key-directory" = let - script = pkgs.writeShellScriptBin "create-waypipe-ssh-public-key-directory" '' - mkdir -pv /run/waypipe-ssh-public-key - chown -v microvm /run/waypipe-ssh-public-key - ''; - in { - enable = true; - description = "Create shared directory on host"; - path = []; - wantedBy = ["microvms.target"]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - StandardOutput = "journal"; - StandardError = "journal"; - ExecStart = "${script}/bin/create-waypipe-ssh-public-key-directory"; + systemd.services."create-waypipe-ssh-public-key-directory" = + let + script = pkgs.writeShellScriptBin "create-waypipe-ssh-public-key-directory" '' + mkdir -pv ${config.ghaf.security.sshKeys.waypipeSshPublicKeyDir} + chown -v microvm ${config.ghaf.security.sshKeys.waypipeSshPublicKeyDir} + ''; + in + { + enable = true; + description = "Create shared directory on host"; + path = [ ]; + wantedBy = [ "microvms.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + StandardOutput = "journal"; + StandardError = "journal"; + ExecStart = "${script}/bin/create-waypipe-ssh-public-key-directory"; + }; }; - }; # Waypipe in GUIVM needs to communicate with AppVMs over VSOCK # However, VSOCK does not support direct guest to guest communication @@ -214,13 +267,13 @@ in { systemd.services.vsockproxy = { enable = true; description = "vsockproxy"; - unitConfig = { - Type = "simple"; - }; serviceConfig = { + Type = "simple"; + Restart = "always"; + RestartSec = "1"; ExecStart = "${vsockproxy}/bin/vsockproxy ${toString cfg.waypipePort} ${toString cfg.vsockCID} ${toString cfg.waypipePort}"; }; - wantedBy = ["multi-user.target"]; + wantedBy = [ "multi-user.target" ]; }; }; } diff --git a/modules/microvm/virtualization/microvm/idsvm/idsvm.nix b/modules/microvm/virtualization/microvm/idsvm/idsvm.nix new file mode 100644 index 000000000..4354e26c0 --- /dev/null +++ b/modules/microvm/virtualization/microvm/idsvm/idsvm.nix @@ -0,0 +1,98 @@ +# Copyright 2022-2023 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: +let + configHost = config; + vmName = "ids-vm"; + macAddress = "02:00:00:01:01:02"; + idsvmBaseConfiguration = { + imports = [ + (import ../common/vm-networking.nix { + inherit + config + lib + vmName + macAddress + ; + internalIP = 4; + }) + ( + { lib, ... }: + { + ghaf = { + users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; + profiles.debug.enable = lib.mkDefault configHost.ghaf.profiles.debug.enable; + + virtualization.microvm.idsvm.mitmproxy.enable = + configHost.ghaf.virtualization.microvm.idsvm.mitmproxy.enable; + + development = { + # NOTE: SSH port also becomes accessible on the network interface + # that has been passed through to NetVM + ssh.daemon.enable = lib.mkDefault configHost.ghaf.development.ssh.daemon.enable; + debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; + nix-setup.enable = lib.mkDefault configHost.ghaf.development.nix-setup.enable; + }; + }; + + system.stateVersion = lib.trivial.release; + + nixpkgs.buildPlatform.system = configHost.nixpkgs.buildPlatform.system; + nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system; + + microvm.hypervisor = "qemu"; + + environment.systemPackages = [ + pkgs.snort # TODO: put into separate module + ] ++ (lib.optional configHost.ghaf.profiles.debug.enable pkgs.tcpdump); + + microvm = { + optimize.enable = true; + shares = [ + { + tag = "ro-store"; + source = "/nix/store"; + mountPoint = "/nix/.ro-store"; + proto = "virtiofs"; + } + ]; + writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; + }; + + imports = [ + ../../../../common + ./mitmproxy + ]; + } + ) + ]; + }; + cfg = config.ghaf.virtualization.microvm.idsvm; +in +{ + options.ghaf.virtualization.microvm.idsvm = { + enable = lib.mkEnableOption "Whether to enable IDS-VM on the system"; + + extraModules = lib.mkOption { + description = '' + List of additional modules to be imported and evaluated as part of + IDSVM's NixOS configuration. + ''; + default = [ ]; + }; + }; + + config = lib.mkIf cfg.enable { + microvm.vms."${vmName}" = { + autostart = true; + config = idsvmBaseConfiguration // { + imports = idsvmBaseConfiguration.imports ++ cfg.extraModules; + }; + }; + }; +} diff --git a/modules/microvm/virtualization/microvm/idsvm/mitmproxy/default.nix b/modules/microvm/virtualization/microvm/idsvm/mitmproxy/default.nix new file mode 100644 index 000000000..3eb43094f --- /dev/null +++ b/modules/microvm/virtualization/microvm/idsvm/mitmproxy/default.nix @@ -0,0 +1,68 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + pkgs, + config, + ... +}: +let + cfg = config.ghaf.virtualization.microvm.idsvm.mitmproxy; + mitmproxyport = 8080; + mitmwebUIport = 8081; +in +{ + options.ghaf.virtualization.microvm.idsvm.mitmproxy = { + enable = lib.mkEnableOption "Whether to enable mitmproxy on ids-vm"; + }; + + config = lib.mkIf cfg.enable { + # Here we add default CA keypair and corresponding self-signed certificate + # for mitmproxy in different formats. These should be, of course, randomly and + # securely generated and stored for each instance, but for development purposes + # we use these fixed ones. + environment.etc = { + "mitmproxy/mitmproxy-ca-cert.cer".source = ./mitmproxy-ca/mitmproxy-ca-cert.cer; + "mitmproxy/mitmproxy-ca-cert.p12".source = ./mitmproxy-ca/mitmproxy-ca-cert.p12; + "mitmproxy/mitmproxy-ca-cert.pem".source = ./mitmproxy-ca/mitmproxy-ca-cert.pem; + "mitmproxy/mitmproxy-ca.pem".source = ./mitmproxy-ca/mitmproxy-ca.pem; + "mitmproxy/mitmproxy-ca.p12".source = ./mitmproxy-ca/mitmproxy-ca.p12; + "mitmproxy/mitmproxy-dhparam.pem".source = ./mitmproxy-ca/mitmproxy-dhparam.pem; + }; + + systemd.services."mitmweb-server" = + let + mitmwebScript = pkgs.writeShellScriptBin "mitmweb-server" '' + ${pkgs.mitmproxy}/bin/mitmweb --web-host localhost --web-port ${toString mitmwebUIport} --set confdir=/etc/mitmproxy + ''; + in + { + enable = true; + description = "Run mitmweb to establish web interface for mitmproxy"; + path = [ mitmwebScript ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + StandardOutput = "journal"; + StandardError = "journal"; + ExecStart = "${mitmwebScript}/bin/mitmweb-server"; + Restart = "on-failure"; + RestartSec = "1"; + }; + }; + + networking = { + firewall.allowedTCPPorts = [ + mitmproxyport + mitmwebUIport + ]; + nat.extraCommands = + # Redirect http(s) traffic to mitmproxy. + '' + iptables -t nat -A PREROUTING -i ethint0 -p tcp --dport 80 -j REDIRECT --to-port ${toString mitmproxyport} + iptables -t nat -A PREROUTING -i ethint0 -p tcp --dport 443 -j REDIRECT --to-port ${toString mitmproxyport} + ''; + }; + environment.systemPackages = [ pkgs.mitmproxy ]; + }; +} diff --git a/modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca-cert.cer b/modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca-cert.cer new file mode 100644 index 000000000..9beb77739 --- /dev/null +++ b/modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca-cert.cer @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNTCCAh2gAwIBAgIUItvWgfGeI8GlhgumoYarXZhO1OMwDQYJKoZIhvcNAQEL +BQAwKDESMBAGA1UEAwwJbWl0bXByb3h5MRIwEAYDVQQKDAltaXRtcHJveHkwHhcN +MjMwNjI2MjA0MjUxWhcNMzMwNjI1MjA0MjUxWjAoMRIwEAYDVQQDDAltaXRtcHJv +eHkxEjAQBgNVBAoMCW1pdG1wcm94eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAOPknE6S+anfr52iO58VsPBcKrTbpWCV1NPmpWh6YmZxuzA3IjNu8X9i +0ByVgutysmrIXWqt7EOds8vCqLCX3+pGB6XsNMC4ksn42SH6QmWUTZizUjCI+7c2 +B1fYxzU5aaG2Z9TDtfExdWqnHR0c0dTR7c2IUeH7qgy/8oSukQeFdhp/j/d+cosU +KtXxMl9vk4wiseLRS2JBb+QKdM+TdNKLpAZmYT68WIIPB/0Vsxo1ZeSf8A4KLElr +9z9oksT5RPZAkuqV4TtWZoSPf01lB5jBCRblSGqw3m9ARAjH3MN1cDvwKkOtPrEC +iBKv9S51CyGPLkrEQoQrscvGKkEp5mECAwEAAaNXMFUwDwYDVR0TAQH/BAUwAwEB +/zATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FLfWC+xt92Gs5X8I0H9E0ZPZ1nUZMA0GCSqGSIb3DQEBCwUAA4IBAQCEuExtxt6S +Pr7hXul8xNl8gjb94xB2vB6DJwtn97vXDtMqQ7P6o9e+7d2Yzp/y/hAlVpkZbwJo +WnE5aKI+SiuoyPJhM3qtSqFEnjogm+2GS+Htd9SGYPX6qrsbG5/FUE2NKF4sr9zB +vNYOzcaJO6X1+A3a7fS65ytjRYwO0T+6NtPkqwJ/ACT3vov94u9oGJ8O9rkFoG93 +7Guyh26JA71/N8SKWSIB/35pYKvX2usmsPCs8UYNC3UH4fH4d0yHBA9vV9XLE5H5 +cgESHG6F13V3WpeEgc83DWG6Tvml64ldORCVSi5doLTfaN/UIEZXFPMZ2ZCfsQvA ++PqFqfsCDYU1 +-----END CERTIFICATE----- diff --git a/modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca-cert.p12 b/modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca-cert.p12 new file mode 100644 index 0000000000000000000000000000000000000000..b7103f08eeac334b8b0ca8f23c5f7da5f6299e46 GIT binary patch literal 1015 zcmXqLV*bp;$ZXKWyqAqrtIebBJ1-+U zZL4Ev_+5EyzzesCk(-slK*LbTK!A-ol!cjxGdHs&x1cD$q7o*?g(PMmC(dhRY+z<& zW@KPuWNH{C&T9Bvu)Zw`}QO?_w`me$f=@0-d# z;~=NlQ;)0-_wpTFIF8>rTv}lLLCblq-A1Mkq4i(&O1TyL^}LQawP!p z*$iYsd_ER27Lo1OxZmV{Ph9h~p5sEj%f-nzua!z72R$$a0E3>9p=F0p?y-B5?Djp3 zds%bjW=)gX-^T)Fd*qtcxzoSzzRq`9%X#y!#n<<}y*uOF{7?S`RKsRU<}+nP6X!;e48ecD<0^TV&67M}RCh*3x;ul0Iq zdDQfl#`EU9iM#xME`8k@Ybh|*OD}f8mirm^uPC^Mi+q;6Ibr@r?gKx5wXXcl#M^3W zsBEAJ&lj8`h9Yu847m)M3?)FafT4&XpP_=G(m>Zh(SVbURhy5QNs5($MP&aQv-u}) edM_;M+SPwYBlY%&y??D)I8KQF50;u+QUU;3qCc-Fe^-+e#Ny08De{tpBbtWY2tf(ha{ zf_$V&Cf^I!9{rsLKt@jLMTx^rspP zyzNMb`azG}YPZ3bhbvU3=Up)|(Z0dHrJiWo)Tbhz#&_YR$@qwX-JywVA&fXddG?CR zd3tKRboq3jxsyJ9mfQb%s(*i6KF-zgaQ$hR7r!M`xm(?3mbFagRy6fm({oR0-7x%k zB;|G5l#Y#Scw(rf3qGTXAUtQ{SvEmGV#w7o-oo_NS(Z1rf4CeK&HrFkiAj`7R5EKl z8m>^@-7c^H<10rL8U(;RTgV#XMJ6LA0D{{XEQCg0kiZU*#qdDfI4%Voh{fX3AVl7Q zygOAGWbimhY$qmTgfW6OL&QbzHOG>5)GGksbuXYc0InCsGbK zO#QrYrIm99*4Uci3#b?WbwANuUzk}|- zA2w4c?0n~_8hqa@*YLKsT=c4l@`*t|3DsNhY|wWUg0WiP%643i z{SgCWWf3feEV1vcj3hD55R7R+fx!1eLHvM$F@;DbAIbdR5&t;iUrIEZpWH~E;0n=; z`YYJwmKTc>QvT?R3_*%^T|?SWtCtj76LCUO#vzUhchnWWXnq*O=$T{AT#s;!`pzEDYGL?&S(Lk+yuP zZJ?;ksoN_4>E1$zXg<3{@siznwV8=)#Q1ghtSf;xNU?Gf!oEK7K%%@>UPy5k!!)kl zEr!b3Gi=|@)|t~1x=>1aP$~19Gkj<3#Qc#{?Oj8$PZo~-;c$B2mE53;`hM##BK}uA zBhE6*_a^w{w`jLqPN9y?@ANP$%O0Ub&B`rn9r5tU%}`nJTm6;e8(1prr~h$;OS$lm zD#<%MN%>Qf8Sk>Rb+r4Sw4hqHb|Xzf+U_I?K-F!toqYFLtVUVnI+Z z&6qK`hnzE&L2;4Kq)yaF;%cqhb!BeyGUqImU3#1@F>(YTcJyZ{_VznQM zX3tf_H!AIVW5#=O93_P%Uyu?5)Fo|QvlDJrWuKo&)oeAb035p}nX${KE;6d~v_7Bc zuU$*(PEg5EW*9ArX$cOvZf-t}Q@G8FD_mN67ODACPLbj)^xW?4#o?%@t?^Cno5t9Y zL-{Qu{bR1d6dBYgOUZ6Ou!Rb{W>u6qbn(e9=I7x7<2WmJ$6cQIFq=xfef?wRe91qY zxC84*-n{*24_@$HPpR&@;`?dFp<-&5nBOI@a_)lE9F)bRe(q_*^dJ@&t$fs{f1Bd{ z(rZ3p$wnvIQk%F-CuTiZ`h11RNvp9WUA+te&7k)lXfquXp}xab6n8COzFL19pl1Vpv+66sH=+Jlxum=_L*r^3isvt)vEdKnP19R4_$B5 zXmATM-6oIjC-=8xF9K+p`IEufxiI+hXV=# literal 0 HcmV?d00001 diff --git a/modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca.pem b/modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca.pem new file mode 100644 index 000000000..b2c545328 --- /dev/null +++ b/modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-ca.pem @@ -0,0 +1,47 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA4+ScTpL5qd+vnaI7nxWw8FwqtNulYJXU0+alaHpiZnG7MDci +M27xf2LQHJWC63Kyashdaq3sQ52zy8KosJff6kYHpew0wLiSyfjZIfpCZZRNmLNS +MIj7tzYHV9jHNTlpobZn1MO18TF1aqcdHRzR1NHtzYhR4fuqDL/yhK6RB4V2Gn+P +935yixQq1fEyX2+TjCKx4tFLYkFv5Ap0z5N00oukBmZhPrxYgg8H/RWzGjVl5J/w +DgosSWv3P2iSxPlE9kCS6pXhO1ZmhI9/TWUHmMEJFuVIarDeb0BECMfcw3VwO/Aq +Q60+sQKIEq/1LnULIY8uSsRChCuxy8YqQSnmYQIDAQABAoIBAQC/S1L5kd4Ifj+H +7nplm2ufF36xuf4kCSFRjjYicTjQDX+3hVAsJGCLMYLHu6jdwrWJdQ8VUVEVoPcf +fxLiyVmn6YjZ+mB9tXFiIIUDRHMfmVFZcIz5OMMykyOu1cTCJKNKnzahHndHMuEA +2a5SlbJ9FoqrEFbLftjLQwRr46zRxduoF2Znz/XhPMcoOsMoFuUIEtS3kmblW8Zr +UzKkvT2GUb5b19WNIbK/1ZWnkYTh6nTQPNz8FYpNb7ZuS/UfNGP05r+ZbgzmSS8B +Mwl2u2AqXEo15ULjEP8XQpmQXDbaOAjZHzF0nqx2Sw7iY9MfAarIekGLVRJ+LRwA +mkT8TPuRAoGBAP+20Ah6SCJN4DpDLC/Zu/2rRanpxxyk1awseFlfNOPegAuM+Gic +fHeUDYooHxZwbowAjyo4o36rnHJJi8ZniTHZG9ddy9U75TgVZK4Xr7MkmmOCpv1Q +50BTxsnWir3pTspgWCZ8oXmyvNJV/hl0fGqFW3WxI41upMM6w3uSMdvnAoGBAOQl +1dgXh+Qo8DhAaWmhmDLpcfWD2XB3rhZxQfbYCC+oyrQgpgyQpOEgmPKcjDrsToRK +Ze08O3t5inrvyH41THhByDfV6pxZSGRPoBxr1ZMej6V50FFHctQbDqDhmBdlKpkx +3ryGBrhUxjwklg915UwvZc1iewYdZxd0JeST+CJ3AoGBALbU9QU6uRyd5baClLNZ +0InczaBhIBYg3Q2PdjUgV2adjZu0nV/ekzfESbIAYcnfdYrwU2xytqM4/FDSuPeQ +y40ymC9yRu0dOBTTZvr6wIsrnp+LqO3xzIY34CgsF2MVz1nvbNeHwMSMwWj6RwXY +PaTD2NLbZnoXJALany5ZJwD9AoGAVKqZ1my9GHX819NHi1TVx6cMjIFWsz8m0ttL +EJERUKaCOyCWnrkbBxTyza48+Czz4nI9qzGcHXF4a7EKpZOgAkzfQaFYRJd5nwhR +sdpu0v8XbeBr543tVjuITToLGDuJ+HoiX7IZUlTbkDw/mBM3efNpAzRV1WoZ9QE8 +grxK7HcCgYAT0dGsFd1RY+m/Ik/jTxRDSi7zLLtyZO8AsGsfqsm0b8GhTTlXzEmH +kgp75/W058vjc7H1PY7FNr5neUn/Dtom2YtJRhANK/dhzh+RDSfFgbCX+VHTwh1a +nb7F25+bEhlvfe5yLb+O6ZzbsL/EdJYg0BoHCgTI2bZJkzRtAzdHuA== +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDNTCCAh2gAwIBAgIUItvWgfGeI8GlhgumoYarXZhO1OMwDQYJKoZIhvcNAQEL +BQAwKDESMBAGA1UEAwwJbWl0bXByb3h5MRIwEAYDVQQKDAltaXRtcHJveHkwHhcN +MjMwNjI2MjA0MjUxWhcNMzMwNjI1MjA0MjUxWjAoMRIwEAYDVQQDDAltaXRtcHJv +eHkxEjAQBgNVBAoMCW1pdG1wcm94eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAOPknE6S+anfr52iO58VsPBcKrTbpWCV1NPmpWh6YmZxuzA3IjNu8X9i +0ByVgutysmrIXWqt7EOds8vCqLCX3+pGB6XsNMC4ksn42SH6QmWUTZizUjCI+7c2 +B1fYxzU5aaG2Z9TDtfExdWqnHR0c0dTR7c2IUeH7qgy/8oSukQeFdhp/j/d+cosU +KtXxMl9vk4wiseLRS2JBb+QKdM+TdNKLpAZmYT68WIIPB/0Vsxo1ZeSf8A4KLElr +9z9oksT5RPZAkuqV4TtWZoSPf01lB5jBCRblSGqw3m9ARAjH3MN1cDvwKkOtPrEC +iBKv9S51CyGPLkrEQoQrscvGKkEp5mECAwEAAaNXMFUwDwYDVR0TAQH/BAUwAwEB +/zATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FLfWC+xt92Gs5X8I0H9E0ZPZ1nUZMA0GCSqGSIb3DQEBCwUAA4IBAQCEuExtxt6S +Pr7hXul8xNl8gjb94xB2vB6DJwtn97vXDtMqQ7P6o9e+7d2Yzp/y/hAlVpkZbwJo +WnE5aKI+SiuoyPJhM3qtSqFEnjogm+2GS+Htd9SGYPX6qrsbG5/FUE2NKF4sr9zB +vNYOzcaJO6X1+A3a7fS65ytjRYwO0T+6NtPkqwJ/ACT3vov94u9oGJ8O9rkFoG93 +7Guyh26JA71/N8SKWSIB/35pYKvX2usmsPCs8UYNC3UH4fH4d0yHBA9vV9XLE5H5 +cgESHG6F13V3WpeEgc83DWG6Tvml64ldORCVSi5doLTfaN/UIEZXFPMZ2ZCfsQvA ++PqFqfsCDYU1 +-----END CERTIFICATE----- diff --git a/modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-dhparam.pem b/modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-dhparam.pem new file mode 100644 index 000000000..c10121fbf --- /dev/null +++ b/modules/microvm/virtualization/microvm/idsvm/mitmproxy/mitmproxy-ca/mitmproxy-dhparam.pem @@ -0,0 +1,14 @@ + +-----BEGIN DH PARAMETERS----- +MIICCAKCAgEAyT6LzpwVFS3gryIo29J5icvgxCnCebcdSe/NHMkD8dKJf8suFCg3 +O2+dguLakSVif/t6dhImxInJk230HmfC8q93hdcg/j8rLGJYDKu3ik6H//BAHKIv +j5O9yjU3rXCfmVJQic2Nne39sg3CreAepEts2TvYHhVv3TEAzEqCtOuTjgDv0ntJ +Gwpj+BJBRQGG9NvprX1YGJ7WOFBP/hWU7d6tgvE6Xa7T/u9QIKpYHMIkcN/l3ZFB +chZEqVlyrcngtSXCROTPcDOQ6Q8QzhaBJS+Z6rcsd7X+haiQqvoFcmaJ08Ks6LQC +ZIL2EtYJw8V8z7C0igVEBIADZBI6OTbuuhDwRw//zU1uq52Oc48CIZlGxTYG/Evq +o9EWAXUYVzWkDSTeBH1r4z/qLPE2cnhtMxbFxuvK53jGB0emy2y1Ei6IhKshJ5qX +IB/aE7SSHyQ3MDHHkCmQJCsOd4Mo26YX61NZ+n501XjqpCBQ2+DfZCBh8Va2wDyv +A2Ryg9SUz8j0AXViRNMJgJrr446yro/FuJZwnQcO3WQnXeqSBnURqKjmqkeFP+d8 +6mk2tqJaY507lRNqtGlLnj7f5RNoBFJDCLBNurVgfvq9TCVWKDIFD4vZRjCrnl6I +rD693XKIHUCWOjMh1if6omGXKHH40QuME2gNa50+YPn1iYDl88uDbbMCAQI= +-----END DH PARAMETERS----- diff --git a/modules/microvm/virtualization/microvm/microvm-host.nix b/modules/microvm/virtualization/microvm/microvm-host.nix index da9a427b2..53f5c275e 100644 --- a/modules/microvm/virtualization/microvm/microvm-host.nix +++ b/modules/microvm/virtualization/microvm/microvm-host.nix @@ -1,35 +1,61 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 +{ inputs }: { config, lib, pkgs, ... -}: let +}: +let cfg = config.ghaf.virtualization.microvm-host; in - with lib; { - options.ghaf.virtualization.microvm-host = { - enable = mkEnableOption "MicroVM Host"; - networkSupport = mkEnableOption "Network support services to run host applications."; - }; +{ + imports = [ + inputs.impermanence.nixosModules.impermanence + inputs.self.nixosModules.givc-host + ]; + options.ghaf.virtualization.microvm-host = { + enable = lib.mkEnableOption "MicroVM Host"; + networkSupport = lib.mkEnableOption "Network support services to run host applications."; + }; - config = mkIf cfg.enable { - microvm.host.enable = true; - ghaf.systemd = { - withName = "host-systemd"; - enable = true; - boot.enable = true; - withPolkit = true; - withTpm2Tss = pkgs.stdenv.hostPlatform.isx86; - withRepart = true; - withFido2 = true; - withCryptsetup = true; - withTimesyncd = cfg.networkSupport; - withNss = cfg.networkSupport; - withResolved = cfg.networkSupport; - withSerial = config.ghaf.profiles.debug.enable; - withDebug = config.ghaf.profiles.debug.enable; - }; + config = lib.mkIf cfg.enable { + microvm.host.enable = true; + ghaf.systemd = { + withName = "host-systemd"; + enable = true; + boot.enable = true; + withAudit = config.ghaf.profiles.debug.enable; + withPolkit = true; + withTpm2Tss = pkgs.stdenv.hostPlatform.isx86; + withRepart = true; + withFido2 = true; + withCryptsetup = true; + withTimesyncd = cfg.networkSupport; + withNss = cfg.networkSupport; + withResolved = cfg.networkSupport; + withSerial = config.ghaf.profiles.debug.enable; + withDebug = config.ghaf.profiles.debug.enable; + withHardenedConfigs = true; }; - } + ghaf.givc.host.enable = true; + + # TODO: remove hardcoded paths + systemd.services."microvm@audio-vm".serviceConfig = + lib.optionalAttrs config.ghaf.virtualization.microvm.audiovm.enable + { + # The + here is a systemd feature to make the script run as root. + ExecStopPost = [ + "+${pkgs.writeShellScript "reload-audio" '' + # The script makes audio device internal state to reset + # This fixes issue of audio device getting into some unexpected + # state when the VM is being shutdown during audio mic recording + echo "1" > /sys/bus/pci/devices/0000:00:1f.3/remove + sleep 0.1 + echo "1" > /sys/bus/pci/devices/0000:00:1f.0/rescan + ''}" + ]; + }; + }; +} diff --git a/modules/microvm/virtualization/microvm/modules.nix b/modules/microvm/virtualization/microvm/modules.nix new file mode 100644 index 000000000..f61200817 --- /dev/null +++ b/modules/microvm/virtualization/microvm/modules.nix @@ -0,0 +1,170 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: +let + inherit (builtins) hasAttr; + inherit (lib) + mkOption + types + optionals + optionalAttrs + ; + + cfg = config.ghaf.virtualization.microvm; + + # Currently only x86 with hw definition supported + inherit (pkgs.stdenv.hostPlatform) isx86; + fullVirtualization = + isx86 && (hasAttr "hardware" config.ghaf) && (hasAttr "devices" config.ghaf.hardware); + + # Hardware devices passthrough modules + deviceModules = optionalAttrs fullVirtualization { + inherit (config.ghaf.hardware.devices) + netvmPCIPassthroughModule + audiovmPCIPassthroughModule + guivmPCIPassthroughModule + guivmVirtioInputHostEvdevModule + ; + }; + + # Kernel configurations + kernelConfigs = optionalAttrs fullVirtualization { inherit (config.ghaf.kernel) guivm audiovm; }; + + # Firmware module + firmwareModule = { + config.ghaf.services.firmware.enable = true; + }; + + # Qemu configuration modules + qemuModules = { + inherit (config.ghaf.qemu) guivm; + }; + + # Service modules + serviceModules = { + # Givc module + givc = { + config.ghaf.givc.enable = config.ghaf.givc.enable; + }; + + # Audio module + audio = optionalAttrs cfg.audiovm.audio { config.ghaf.services.audio.enable = true; }; + + # Wifi module + wifi = optionalAttrs cfg.netvm.wifi { config.ghaf.services.wifi.enable = true; }; + + # Fprint module + fprint = optionalAttrs cfg.guivm.fprint { config.ghaf.services.fprint.enable = true; }; + + # Desktop module + desktop = { + config.ghaf.services.desktop.enable = true; + }; + + # PDF opener + pdfOpener = { + config.ghaf.services.pdfopener.enable = true; + }; + + # Yubikey module + yubikey = optionalAttrs cfg.guivm.yubikey { config.ghaf.services.yubikey.enable = true; }; + + # Common namespace to share (built-time) between host and VMs + commonNamespace = { + config.ghaf.namespaces = config.ghaf.namespaces; + }; + }; + + # Reference services module + referenceServiceModule = { + config.ghaf = optionalAttrs (hasAttr "reference" config.ghaf) { + reference = optionalAttrs (hasAttr "services" config.ghaf.reference) { + inherit (config.ghaf.reference) services; + }; + }; + }; + + # Reference programs module + referenceProgramsModule = { + config.ghaf = optionalAttrs (hasAttr "reference" config.ghaf) { + reference = optionalAttrs (hasAttr "programs" config.ghaf.reference) { + inherit (config.ghaf.reference) programs; + }; + }; + }; +in +{ + options.ghaf.virtualization.microvm = { + netvm.wifi = mkOption { + type = types.bool; + default = isx86 && cfg.netvm.enable; + description = '' + Enable Wifi module configuration. + ''; + }; + audiovm.audio = mkOption { + type = types.bool; + default = cfg.audiovm.enable; + description = '' + Enable Audio module configuration. + ''; + }; + guivm.fprint = mkOption { + type = types.bool; + default = cfg.guivm.enable; + description = '' + Enable Fingerprint module configuration. + ''; + }; + guivm.yubikey = mkOption { + type = types.bool; + default = cfg.guivm.enable; + description = '' + Enable Yubikey module configuration. + ''; + }; + }; + + config = { + # System VM configurations + ghaf.virtualization.microvm = optionalAttrs fullVirtualization { + # Netvm modules + netvm.extraModules = optionals cfg.netvm.enable [ + deviceModules.netvmPCIPassthroughModule + firmwareModule + serviceModules.wifi + serviceModules.givc + referenceServiceModule + ]; + # Audiovm modules + audiovm.extraModules = optionals cfg.audiovm.enable [ + deviceModules.audiovmPCIPassthroughModule + kernelConfigs.audiovm + serviceModules.audio + serviceModules.givc + ]; + # Guivm modules + guivm.extraModules = optionals cfg.guivm.enable [ + deviceModules.guivmPCIPassthroughModule + deviceModules.guivmVirtioInputHostEvdevModule + kernelConfigs.guivm + firmwareModule + qemuModules.guivm + serviceModules.desktop + serviceModules.fprint + serviceModules.yubikey + serviceModules.pdfOpener + serviceModules.commonNamespace + serviceModules.givc + referenceProgramsModule + ]; + adminvm.extraModules = optionals cfg.adminvm.enable [ serviceModules.givc ]; + appvm.extraModules = optionals cfg.appvm.enable [ serviceModules.givc ]; + }; + }; +} diff --git a/modules/microvm/virtualization/microvm/netvm.nix b/modules/microvm/virtualization/microvm/netvm.nix index 680bf2831..b22ace700 100644 --- a/modules/microvm/virtualization/microvm/netvm.nix +++ b/modules/microvm/virtualization/microvm/netvm.nix @@ -1,109 +1,156 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 +{ inputs }: { config, lib, pkgs, ... -}: let - configHost = config; +}: +let vmName = "net-vm"; macAddress = "02:00:00:01:01:01"; + + isGuiVmEnabled = config.ghaf.virtualization.microvm.guivm.enable; + + sshKeysHelper = pkgs.callPackage ../../../../packages/ssh-keys-helper { + inherit pkgs; + inherit config; + }; + netvmBaseConfiguration = { imports = [ - (import ./common/vm-networking.nix {inherit vmName macAddress;}) - ({lib, ...}: { - ghaf = { - users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; - development = { - # NOTE: SSH port also becomes accessible on the network interface - # that has been passed through to NetVM - ssh.daemon.enable = lib.mkDefault configHost.ghaf.development.ssh.daemon.enable; - debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; - nix-setup.enable = lib.mkDefault configHost.ghaf.development.nix-setup.enable; + inputs.impermanence.nixosModules.impermanence + inputs.self.nixosModules.givc-netvm + (import ./common/vm-networking.nix { + inherit + config + lib + vmName + macAddress + ; + internalIP = 1; + isGateway = true; + }) + + ./common/storagevm.nix + + # To push logs to central location + ../../../common/logging/client.nix + ( + { lib, ... }: + { + imports = [ ../../../common ]; + + ghaf = { + users.accounts.enable = lib.mkDefault config.ghaf.users.accounts.enable; + profiles.debug.enable = lib.mkDefault config.ghaf.profiles.debug.enable; + development = { + # NOTE: SSH port also becomes accessible on the network interface + # that has been passed through to NetVM + ssh.daemon.enable = lib.mkDefault config.ghaf.development.ssh.daemon.enable; + debug.tools.enable = lib.mkDefault config.ghaf.development.debug.tools.enable; + nix-setup.enable = lib.mkDefault config.ghaf.development.nix-setup.enable; + }; + systemd = { + enable = true; + withName = "netvm-systemd"; + withAudit = config.ghaf.profiles.debug.enable; + withPolkit = true; + withResolved = true; + withTimesyncd = true; + withDebug = config.ghaf.profiles.debug.enable; + withHardenedConfigs = true; + }; + givc.netvm.enable = true; + # Logging client configuration + logging.client.enable = config.ghaf.logging.client.enable; + logging.client.endpoint = config.ghaf.logging.client.endpoint; + storagevm = { + enable = true; + name = "netvm"; + directories = [ "/etc/NetworkManager/system-connections/" ]; + }; }; - systemd = { - enable = true; - withName = "netvm-systemd"; - withPolkit = true; - withDebug = configHost.ghaf.profiles.debug.enable; + + time.timeZone = config.time.timeZone; + system.stateVersion = lib.trivial.release; + + nixpkgs = { + buildPlatform.system = config.nixpkgs.buildPlatform.system; + hostPlatform.system = config.nixpkgs.hostPlatform.system; }; - }; - - system.stateVersion = lib.trivial.release; - - nixpkgs.buildPlatform.system = configHost.nixpkgs.buildPlatform.system; - nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system; - - microvm.hypervisor = "qemu"; - - networking = { - firewall.allowedTCPPorts = [53]; - firewall.allowedUDPPorts = [53]; - }; - - # Add simple wi-fi connection helper - environment.systemPackages = lib.mkIf config.ghaf.profiles.debug.enable [pkgs.wifi-connector]; - - # Dnsmasq is used as a DHCP/DNS server inside the NetVM - services.dnsmasq = { - enable = true; - resolveLocalQueries = true; - settings = { - server = ["8.8.8.8"]; - dhcp-range = ["192.168.100.2,192.168.100.254"]; - dhcp-sequential-ip = true; - dhcp-authoritative = true; - domain = "ghaf"; - listen-address = ["127.0.0.1,192.168.100.1"]; - dhcp-option = [ - "option:router,192.168.100.1" - "6,192.168.100.1" - ]; - expand-hosts = true; - domain-needed = true; - bogus-priv = true; + + networking = { + firewall.allowedTCPPorts = [ 53 ]; + firewall.allowedUDPPorts = [ 53 ]; }; - }; - - # Disable resolved since we are using Dnsmasq - services.resolved.enable = false; - - systemd.network = { - enable = true; - networks."10-ethint0" = { - matchConfig.MACAddress = macAddress; - addresses = [ - { - addressConfig.Address = "192.168.100.1/24"; - } - { - # IP-address for debugging subnet - addressConfig.Address = "192.168.101.1/24"; - } - ]; - linkConfig.ActivationPolicy = "always-up"; + + services.openssh = config.ghaf.security.sshKeys.sshAuthorizedKeysCommand; + + # WORKAROUND: Create a rule to temporary hardcode device name for Wi-Fi adapter on x86 + # TODO this is a dirty hack to guard against adding this to Nvidia/vm targets which + # dont have that definition structure yet defined. FIXME. + # TODO the hardware.definition should not even be exposed in targets that do not consume it + services.udev.extraRules = lib.mkIf (config.ghaf.hardware.definition.network.pciDevices != [ ]) '' + SUBSYSTEM=="net", ACTION=="add", ATTRS{vendor}=="0x${(lib.head config.ghaf.hardware.definition.network.pciDevices).vendorId}", ATTRS{device}=="0x${(lib.head config.ghaf.hardware.definition.network.pciDevices).productId}", NAME="${(lib.head config.ghaf.hardware.definition.network.pciDevices).name}" + ''; + + microvm = { + # Optimize is disabled because when it is enabled, qemu is built without libusb + optimize.enable = false; + hypervisor = "qemu"; + shares = + [ + { + tag = "ro-store"; + source = "/nix/store"; + mountPoint = "/nix/.ro-store"; + } + ] + ++ lib.optionals isGuiVmEnabled [ + { + # Add the waypipe-ssh public key to the microvm + tag = config.ghaf.security.sshKeys.waypipeSshPublicKeyName; + source = config.ghaf.security.sshKeys.waypipeSshPublicKeyDir; + mountPoint = config.ghaf.security.sshKeys.waypipeSshPublicKeyDir; + } + ]; + + writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; + qemu = { + machine = + { + # Use the same machine type as the host + x86_64-linux = "q35"; + aarch64-linux = "virt"; + } + .${config.nixpkgs.hostPlatform.system}; + extraArgs = [ + "-device" + "qemu-xhci" + ]; + }; }; - }; - - microvm = { - optimize.enable = true; - shares = [ - { - tag = "ro-store"; - source = "/nix/store"; - mountPoint = "/nix/.ro-store"; - } - ]; - writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store"; - }; - - imports = [../../../common]; - }) + + fileSystems = lib.mkIf isGuiVmEnabled { + ${config.ghaf.security.sshKeys.waypipeSshPublicKeyDir}.options = [ "ro" ]; + }; + + # SSH is very picky about to file permissions and ownership and will + # accept neither direct path inside /nix/store or symlink that points + # there. Therefore we copy the file to /etc/ssh/get-auth-keys (by + # setting mode), instead of symlinking it. + environment.etc = lib.mkIf isGuiVmEnabled { + ${config.ghaf.security.sshKeys.getAuthKeysFilePathInEtc} = sshKeysHelper.getAuthKeysSource; + }; + } + ) ]; }; cfg = config.ghaf.virtualization.microvm.netvm; -in { +in +{ options.ghaf.virtualization.microvm.netvm = { enable = lib.mkEnableOption "NetVM"; @@ -112,21 +159,17 @@ in { List of additional modules to be imported and evaluated as part of NetVM's NixOS configuration. ''; - default = []; + default = [ ]; }; }; config = lib.mkIf cfg.enable { microvm.vms."${vmName}" = { - autostart = false; - # autostart = true; - config = - netvmBaseConfiguration - // { - imports = - netvmBaseConfiguration.imports - ++ cfg.extraModules; - }; + autostart = true; + restartIfChanged = false; + config = netvmBaseConfiguration // { + imports = netvmBaseConfiguration.imports ++ cfg.extraModules; + }; }; }; } diff --git a/modules/polarfire/default.nix b/modules/polarfire/default.nix index 3f64e4f29..dc1dfc364 100644 --- a/modules/polarfire/default.nix +++ b/modules/polarfire/default.nix @@ -3,8 +3,4 @@ # # Support for Microchip Polarfire Icicle-Kit # -{ - imports = [ - ./mpfs-nixos-sdimage.nix - ]; -} +{ imports = [ ./mpfs-nixos-sdimage.nix ]; } diff --git a/modules/polarfire/mpfs-nixos-sdimage.nix b/modules/polarfire/mpfs-nixos-sdimage.nix index 8b2676e32..59ad28e9c 100644 --- a/modules/polarfire/mpfs-nixos-sdimage.nix +++ b/modules/polarfire/mpfs-nixos-sdimage.nix @@ -6,10 +6,9 @@ pkgs, modulesPath, ... -}: { - imports = [ - (modulesPath + "/installer/sd-card/sd-image.nix") - ]; +}: +{ + imports = [ (modulesPath + "/installer/sd-card/sd-image.nix") ]; sdImage = { compressImage = false; diff --git a/modules/reference/appvms/appflowy.nix b/modules/reference/appvms/appflowy.nix new file mode 100644 index 000000000..584b08852 --- /dev/null +++ b/modules/reference/appvms/appflowy.nix @@ -0,0 +1,28 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + lib, + pkgs, + config, + ... +}: +{ + name = "appflowy"; + packages = [ pkgs.appflowy ]; + macAddress = "02:00:00:03:08:01"; + ramMb = 768; + cores = 1; + extraModules = [ + { + hardware.graphics.enable = true; + time.timeZone = config.time.timeZone; + ghaf.givc.appvm = { + enable = true; + name = lib.mkForce "appflowy-vm"; + applications = lib.mkForce ''{"appflowy": "${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/appflowy"}''; + }; + } + ]; + borderColor = "#4c3f7a"; +} diff --git a/modules/reference/appvms/business.nix b/modules/reference/appvms/business.nix new file mode 100644 index 000000000..2fe8908bb --- /dev/null +++ b/modules/reference/appvms/business.nix @@ -0,0 +1,296 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + pkgs, + config, + lib, + ... +}: +let + #TODO: Move this to a common place + xdgPdfPort = 1200; + name = "business"; +in +{ + name = "${name}"; + packages = + let + # PDF XDG handler is executed when the user opens a PDF file in the browser + # The xdgopenpdf script sends a command to the guivm with the file path over TCP connection + xdgPdfItem = pkgs.makeDesktopItem { + name = "ghaf-pdf"; + desktopName = "Ghaf PDF handler"; + exec = "${xdgOpenPdf}/bin/xdgopenpdf %u"; + mimeTypes = [ "application/pdf" ]; + }; + xdgOpenPdf = pkgs.writeShellScriptBin "xdgopenpdf" '' + filepath=$(realpath "$1") + echo "Opening $filepath" | systemd-cat -p info + echo $filepath | ${pkgs.netcat}/bin/nc -N gui-vm ${toString xdgPdfPort} + ''; + in + [ + pkgs.chromium + pkgs.pulseaudio + pkgs.xdg-utils + xdgPdfItem + xdgOpenPdf + pkgs.globalprotect-openconnect + pkgs.openconnect + pkgs.nftables + ]; + # TODO create a repository of mac addresses to avoid conflicts + macAddress = "02:00:00:03:10:01"; + ramMb = 3072; + cores = 4; + extraModules = [ + { + imports = [ ../programs/chromium.nix ]; + # Enable pulseaudio for Chromium VM + security.rtkit.enable = true; + users.extraUsers.ghaf.extraGroups = [ + "audio" + "video" + ]; + + hardware.pulseaudio = { + enable = true; + extraConfig = '' + load-module module-tunnel-sink sink_name=chromium-speaker server=audio-vm:4713 format=s16le channels=2 rate=48000 + load-module module-tunnel-source source_name=chromium-mic server=audio-vm:4713 format=s16le channels=1 rate=48000 + + # Set sink and source default max volume to about 90% (0-65536) + set-sink-volume chromium-speaker 60000 + set-source-volume chromium-mic 60000 + ''; + }; + + time.timeZone = config.time.timeZone; + + microvm = { + qemu.extraArgs = lib.optionals ( + config.ghaf.hardware.usb.internal.enable + && (lib.hasAttr "cam0" config.ghaf.hardware.usb.internal.qemuExtraArgs) + ) config.ghaf.hardware.usb.internal.qemuExtraArgs.cam0; + devices = [ ]; + }; + + ghaf.givc.appvm = { + enable = true; + name = lib.mkForce "business-vm"; + applications = lib.mkForce '' + { + "chromium": "${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/chromium --enable-features=UseOzonePlatform --ozone-platform=wayland ${config.ghaf.givc.idsExtraArgs}", + "outlook": "${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/chromium --enable-features=UseOzonePlatform --ozone-platform=wayland --app=https://outlook.office.com/mail/ ${config.ghaf.givc.idsExtraArgs}", + "office": "${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/chromium --enable-features=UseOzonePlatform --ozone-platform=wayland --app=https://microsoft365.com ${config.ghaf.givc.idsExtraArgs}", + "teams": "${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/chromium --enable-features=UseOzonePlatform --ozone-platform=wayland --app=https://teams.microsoft.com ${config.ghaf.givc.idsExtraArgs}", + "gpclient": "${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/gpclient -platform wayland" + }''; + }; + + ghaf.reference.programs.chromium.enable = true; + ghaf.storagevm = { + enable = true; + name = "${name}"; + users.${config.ghaf.users.accounts.user}.directories = [ ".config" ]; + }; + + # Set default PDF XDG handler + xdg.mime.defaultApplications."application/pdf" = "ghaf-pdf.desktop"; + + # TODO: Add a way to configure the gpclient + # also check the openconnect cli options https://discourse.nixos.org/t/globalprotect-vpn/24014/5 + services.globalprotect = { + enable = true; + csdWrapper = "${pkgs.openconnect}/libexec/openconnect/hipreport.sh"; + }; + + #Firewall Settings + networking = { + firewall.enable = true; + firewall.extraCommands = '' + + iptables -F + add_rule() { + local ip=$1 + iptables -I OUTPUT -p tcp -d $ip --dport 80 -j ACCEPT + iptables -I OUTPUT -p tcp -d $ip --dport 443 -j ACCEPT + iptables -I INPUT -p tcp -s $ip --sport 80 -j ACCEPT + iptables -I INPUT -p tcp -s $ip --sport 443 -j ACCEPT + } + # Urls can be found from Source: https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges + # Allow microsoft365.com + add_rule 13.107.6.156 + add_rule 13.107.9.156 + + # Exchange + add_rule 13.107.6.152/31 + add_rule 13.107.18.10/31 + add_rule 13.107.128.0/22 + add_rule 23.103.160.0/20 + add_rule 40.96.0.0/13 + add_rule 40.104.0.0/15 + add_rule 52.96.0.0/14 + add_rule 131.253.33.215/32 + add_rule 132.245.0.0/16 + add_rule 150.171.32.0/22 + add_rule 204.79.197.215/32 + + + # Exchange Online + add_rule 40.92.0.0/15 + add_rule 40.107.0.0/16 + add_rule 52.100.0.0/14 + add_rule 52.238.78.88/32 + add_rule 104.47.0.0/17 + + + # Sharepoint + add_rule 13.107.136.0/22 + add_rule 40.108.128.0/17 + add_rule 52.104.0.0/14 + add_rule 104.146.128.0/17 + add_rule 150.171.40.0/22 + + + # Common + add_rule 13.107.6.171/32 + add_rule 13.107.18.15/32 + add_rule 13.107.140.6/32 + add_rule 52.108.0.0/14 + add_rule 52.244.37.168/32 + add_rule 20.20.32.0/19 + add_rule 20.190.128.0/18 + add_rule 20.231.128.0/19 + add_rule 40.126.0.0/18 + add_rule 13.107.6.192/32 + add_rule 13.107.9.192/32 + add_rule 52.108.0.0/14 + + # Teams + add_rule 13.107.64.0/18 + add_rule 52.112.0.0/14 + add_rule 52.122.0.0/15 + add_rule 52.108.0.0/14 + add_rule 52.238.119.141/32 + add_rule 52.244.160.207/32 + add_rule 2.16.234.57 + add_rule 23.56.21.152 + add_rule 23.33.233.129 + add_rule 52.123.0.0/16 + + + # Allow VPN access.tii.ae and iservice + add_rule 151.253.154.18 + add_rule 10.161.10.120 + + # To be checked + # Allow res.cdn.office.net + add_rule 152.199.21.175 + add_rule 152.199.39.108 + add_rule 2.21.231.0/24 + add_rule 2.20.249.0/24 + add_rule 152.199.0.0/16 + + + # Allow js.monitor.azure.com + add_rule 13.107.246.0/24 + + # Allow c.s-microsoft.com + add_rule 23.207.193.242 + add_rule 23.208.213.121 + add_rule 23.208.173.122 + add_rule 23.44.1.243 + add_rule 104.65.229.0/24 + add_rule 23.53.113.0/24 + add_rule 2.19.105.47 + + # Allow microsoft.com + add_rule 20.70.246.20 + add_rule 20.236.44.162 + add_rule 20.76.201.171 + add_rule 20.231.239.246 + add_rule 20.112.250.133 + add_rule 184.25.221.172 + + # statics.teams.cdn.office.net + add_rule 95.101.0.0/16 + add_rule 184.87.193.0/24 + add_rule 23.44.0.0/14 + add_rule 96.16.53.0/24 + add_rule 23.59.80.0/24 + add_rule 23.202.33.0/24 + add_rule 104.73.172.0/24 + add_rule 184.27.123.0/24 + add_rule 2.16.56.0/24 + add_rule 23.219.73.130 + add_rule 104.93.18.174 + add_rule 2.21.225.158 + add_rule 23.45.137.145 + add_rule 23.48.121.167 + add_rule 23.46.197.94 + add_rule 104.80.21.47 + add_rule 23.195.154.8 + add_rule 193.229.113.0/24 + + # edge.skype.com for teams + add_rule 13.107.254.0/24 + add_rule 13.107.3.0/24 + + # api.flightproxy.skype.com for teams + add_rule 98.66.0.0/16 + add_rule 4.208.0.0/16 + add_rule 4.225.208.0/24 + add_rule 4.210.0.0/16 + add_rule 108.141.240.0/24 + add_rule 74.241.0.0/16 + add_rule 20.216.0.0/16 + add_rule 172.211.0.0/16 + add_rule 20.50.217.0/24 + add_rule 68.219.14.0/24 + add_rule 20.107.136.0/24 + add_rule 4.175.191.0/24 + add_rule 98.64.0.0/16 + + # Allow tiiuae.sharepoint.com + add_rule 52.104.7.53 + add_rule 52.105.255.39 + add_rule 13.107.138.10 + add_rule 13.107.136.10 + add_rule 118.215.84.0/24 + add_rule 104.69.171.0/24 + add_rule 13.107.136.10 + add_rule 23.15.111.0/24 + # Allow shell.cdn.office.net + add_rule 23.50.92.176 + add_rule 23.15.30.57 + add_rule 23.50.187.58 + add_rule 104.73.234.244 + add_rule 104.83.143.131 + # Allow res-1.cdn.office.net + add_rule 23.52.40.0/24 + add_rule 23.64.122.0/24 + add_rule 2.16.106.0/24 + # Allow publiccdn.sharepointonline.com + add_rule 23.50.86.117 + add_rule 104.69.168.125 + add_rule 2.16.43.238 + add_rule 23.34.79.0/24 + add_rule 23.39.68.0/24 + # r4.res.office365.com + add_rule 2.19.97.32 + add_rule 2.22.61.139 + + + # Block all other HTTP and HTTPS traffic + iptables -A OUTPUT -p tcp --dport 80 -j REJECT + iptables -A OUTPUT -p tcp --dport 443 -j REJECT + + ''; + }; + } + ]; + borderColor = "#00FF00"; + vtpm.enable = true; +} diff --git a/modules/reference/appvms/chromium.nix b/modules/reference/appvms/chromium.nix new file mode 100644 index 000000000..2b7094720 --- /dev/null +++ b/modules/reference/appvms/chromium.nix @@ -0,0 +1,96 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + pkgs, + lib, + config, + ... +}: +let + inherit (lib) hasAttr optionals; + xdgPdfPort = 1200; + name = "chromium"; +in +{ + name = "${name}"; + packages = + let + # PDF XDG handler is executed when the user opens a PDF file in the browser + # The xdgopenpdf script sends a command to the guivm with the file path over TCP connection + xdgPdfItem = pkgs.makeDesktopItem { + name = "ghaf-pdf"; + desktopName = "Ghaf PDF handler"; + exec = "${xdgOpenPdf}/bin/xdgopenpdf %u"; + mimeTypes = [ "application/pdf" ]; + }; + xdgOpenPdf = pkgs.writeShellScriptBin "xdgopenpdf" '' + filepath=$(realpath "$1") + echo "Opening $filepath" | systemd-cat -p info + echo $filepath | ${pkgs.netcat}/bin/nc -N gui-vm ${toString xdgPdfPort} + ''; + in + [ + pkgs.chromium + pkgs.pulseaudio + pkgs.xdg-utils + xdgPdfItem + xdgOpenPdf + ]; + # TODO create a repository of mac addresses to avoid conflicts + macAddress = "02:00:00:03:05:01"; + ramMb = 3072; + cores = 4; + extraModules = [ + { + imports = [ ../programs/chromium.nix ]; + # Enable pulseaudio for Chromium VM + security.rtkit.enable = true; + users.extraUsers.ghaf.extraGroups = [ + "audio" + "video" + ]; + + hardware.pulseaudio = { + enable = true; + extraConfig = '' + load-module module-tunnel-sink sink_name=chromium-speaker server=audio-vm:4713 format=s16le channels=2 rate=48000 + load-module module-tunnel-source source_name=chromium-mic server=audio-vm:4713 format=s16le channels=1 rate=48000 + + # Set sink and source default max volume to about 90% (0-65536) + set-sink-volume chromium-speaker 60000 + set-source-volume chromium-mic 60000 + ''; + }; + + time.timeZone = config.time.timeZone; + + microvm.qemu.extraArgs = optionals ( + config.ghaf.hardware.usb.internal.enable + && (hasAttr "cam0" config.ghaf.hardware.usb.internal.qemuExtraArgs) + ) config.ghaf.hardware.usb.internal.qemuExtraArgs.cam0; + microvm.devices = [ ]; + + ghaf.givc.appvm = { + enable = true; + name = lib.mkForce "chromium-vm"; + applications = lib.mkForce '' + { + "chromium": "${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/chromium --enable-features=UseOzonePlatform --ozone-platform=wayland ${config.ghaf.givc.idsExtraArgs}" + }''; + }; + + ghaf.reference.programs.chromium.enable = true; + ghaf.storagevm = { + enable = true; + name = "${name}"; + users.${config.ghaf.users.accounts.user}.directories = [ ".config" ]; + }; + + # Set default PDF XDG handler + xdg.mime.defaultApplications."application/pdf" = "ghaf-pdf.desktop"; + } + ]; + borderColor = "#630505"; + vtpm.enable = true; +} diff --git a/modules/reference/appvms/comms.nix b/modules/reference/appvms/comms.nix new file mode 100644 index 000000000..78dcc0055 --- /dev/null +++ b/modules/reference/appvms/comms.nix @@ -0,0 +1,115 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + lib, + pkgs, + config, + ... +}: +let + inherit (lib) hasAttr optionals; + dendrite-pinecone = pkgs.callPackage ../../../packages/dendrite-pinecone { }; + isDendritePineconeEnabled = + if (hasAttr "services" config.ghaf.reference) then + config.ghaf.reference.services.dendrite + else + false; +in +{ + name = "comms"; + + packages = [ + pkgs.chromium + pkgs.element-desktop + pkgs.element-gps + pkgs.gpsd + pkgs.tcpdump + pkgs.pulseaudio + ] ++ pkgs.lib.optionals isDendritePineconeEnabled [ dendrite-pinecone ]; + macAddress = "02:00:00:03:09:01"; + ramMb = 4096; + cores = 4; + extraModules = [ + { + # Enable pulseaudio for user ghaf to access mic + security.rtkit.enable = true; + users.extraUsers.ghaf.extraGroups = [ + "audio" + "video" + ]; + + hardware.pulseaudio = { + enable = true; + extraConfig = '' + load-module module-tunnel-sink sink_name=element-speaker server=audio-vm:4713 format=s16le channels=2 rate=48000 + load-module module-tunnel-source source_name=element-mic server=audio-vm:4713 format=s16le channels=1 rate=48000 + + # Set sink and source default max volume to about 90% (0-65536) + set-sink-volume element-speaker 60000 + set-source-volume element-mic 60000 + ''; + }; + + systemd = { + services = { + element-gps = { + description = "Element-gps is a GPS location provider for Element websocket interface."; + enable = true; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.element-gps}/bin/main.py"; + Restart = "on-failure"; + RestartSec = "2"; + }; + wantedBy = [ "multi-user.target" ]; + }; + + "dendrite-pinecone" = pkgs.lib.mkIf isDendritePineconeEnabled { + description = "Dendrite is a second-generation Matrix homeserver with Pinecone which is a next-generation P2P overlay network"; + enable = true; + serviceConfig = { + Type = "simple"; + ExecStart = "${dendrite-pinecone}/bin/dendrite-demo-pinecone"; + Restart = "on-failure"; + RestartSec = "2"; + }; + wantedBy = [ "multi-user.target" ]; + }; + }; + }; + + networking = pkgs.lib.mkIf isDendritePineconeEnabled { + firewall.allowedTCPPorts = [ dendrite-pinecone.TcpPortInt ]; + firewall.allowedUDPPorts = [ dendrite-pinecone.McastUdpPortInt ]; + }; + + time.timeZone = config.time.timeZone; + + services.gpsd = { + enable = true; + devices = [ "/dev/ttyUSB0" ]; + readonly = true; + debugLevel = 2; + listenany = true; + extraArgs = [ "-n" ]; # Do not wait for a client to connect before polling + }; + + microvm.qemu.extraArgs = optionals ( + config.ghaf.hardware.usb.external.enable + && (hasAttr "gps0" config.ghaf.hardware.usb.external.qemuExtraArgs) + ) config.ghaf.hardware.usb.external.qemuExtraArgs.gps0; + + ghaf.givc.appvm = { + enable = true; + name = lib.mkForce "comms-vm"; + applications = lib.mkForce '' + { + "element": "${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/element-desktop --enable-features=UseOzonePlatform --ozone-platform=wayland", + "slack": "${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/chromium --enable-features=UseOzonePlatform --ozone-platform=wayland --app=https://app.slack.com/client ${config.ghaf.givc.idsExtraArgs}" + }''; + }; + } + ]; + borderColor = "#337aff"; +} diff --git a/modules/reference/appvms/default.nix b/modules/reference/appvms/default.nix new file mode 100644 index 000000000..9e2664aea --- /dev/null +++ b/modules/reference/appvms/default.nix @@ -0,0 +1,47 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.ghaf.reference.appvms; +in +{ + imports = [ ]; + + options.ghaf.reference.appvms = { + enable = lib.mkEnableOption "Enable the Ghaf reference appvms module"; + chromium-vm = lib.mkEnableOption "Enable the Chromium appvm"; + gala-vm = lib.mkEnableOption "Enable the Gala appvm"; + zathura-vm = lib.mkEnableOption "Enable the Zathura appvm"; + comms-vm = lib.mkEnableOption '' + Enable the communications appvm + - Element + - Slack + ''; + appflowy-vm = lib.mkEnableOption "Enable the Appflowy appvm"; + business-vm = lib.mkEnableOption "Enable the Business appvm"; + enabled-app-vms = lib.mkOption { + type = lib.types.listOf lib.types.attrs; + default = [ ]; + description = '' + List of appvms to include in the Ghaf reference appvms module + ''; + }; + }; + + config = lib.mkIf cfg.enable { + ghaf.reference.appvms = { + enabled-app-vms = + (lib.optionals cfg.chromium-vm [ (import ./chromium.nix { inherit pkgs lib config; }) ]) + ++ (lib.optionals cfg.gala-vm [ (import ./gala.nix { inherit pkgs lib config; }) ]) + ++ (lib.optionals cfg.zathura-vm [ (import ./zathura.nix { inherit pkgs lib config; }) ]) + ++ (lib.optionals cfg.comms-vm [ (import ./comms.nix { inherit pkgs lib config; }) ]) + ++ (lib.optionals cfg.appflowy-vm [ (import ./appflowy.nix { inherit pkgs lib config; }) ]) + ++ (lib.optionals cfg.business-vm [ (import ./business.nix { inherit pkgs lib config; }) ]); + }; + }; +} diff --git a/modules/reference/appvms/gala.nix b/modules/reference/appvms/gala.nix new file mode 100644 index 000000000..b895b7ae1 --- /dev/null +++ b/modules/reference/appvms/gala.nix @@ -0,0 +1,27 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + lib, + pkgs, + config, + ... +}: +{ + name = "gala"; + packages = [ pkgs.gala-app ]; + macAddress = "02:00:00:03:06:01"; + ramMb = 1536; + cores = 2; + extraModules = [ + { + time.timeZone = config.time.timeZone; + ghaf.givc.appvm = { + enable = true; + name = lib.mkForce "gala-vm"; + applications = lib.mkForce ''{"gala": "${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/gala --enable-features=UseOzonePlatform --ozone-platform=wayland"}''; + }; + } + ]; + borderColor = "#027d7b"; +} diff --git a/modules/reference/appvms/zathura.nix b/modules/reference/appvms/zathura.nix new file mode 100644 index 000000000..d8f7a7d52 --- /dev/null +++ b/modules/reference/appvms/zathura.nix @@ -0,0 +1,29 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + lib, + pkgs, + config, + ... +}: +{ + name = "zathura"; + packages = [ pkgs.zathura ]; + macAddress = "02:00:00:03:07:01"; + ramMb = 512; + cores = 1; + extraModules = [ + { + imports = [ ../programs/zathura.nix ]; + time.timeZone = config.time.timeZone; + ghaf.reference.programs.zathura.enable = true; + ghaf.givc.appvm = { + enable = true; + name = lib.mkForce "zathura-vm"; + applications = lib.mkForce ''{"zathura": "${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/zathura"}''; + }; + } + ]; + borderColor = "#122263"; +} diff --git a/modules/common/development/authorized_ssh_keys.nix b/modules/reference/personalize/authorizedSshKeys.nix similarity index 72% rename from modules/common/development/authorized_ssh_keys.nix rename to modules/reference/personalize/authorizedSshKeys.nix index 0056d4c10..5bdff3b50 100644 --- a/modules/common/development/authorized_ssh_keys.nix +++ b/modules/reference/personalize/authorizedSshKeys.nix @@ -1,7 +1,7 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 { - authorizedKeys = [ + authorizedSshKeys = [ # Add your SSH Public Keys here # NOTE: adding your pub ssh key here will make accessing and "nixos-rebuild switching" development mode # builds easy but still secure. Given that you protect your private keys. Do not share your keypairs across hosts. @@ -13,9 +13,14 @@ # You have been helped and you have been warned. "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIA/pwHnzGNM+ZU4lANGROTRe2ZHbes7cnZn72Oeun/MCAAAABHNzaDo= brian@arcadia" "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEJ9ewKwo5FLj6zE30KnTn8+nw7aKdei9SeTwaAeRdJDAAAABHNzaDo= brian@minerva" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILu6O3swRVWAjP7J8iYGT6st7NAa+o/XaemokmtKdpGa brian@builder" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDdNDuKwAsAff4iFRfujo77W4cyAbfQHjHP57h/7tJde ville.ilvonen@unikie.com" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICKm9NtS/ZmrxQhY/pbRlX+9O1VaBEd8D9vojDtvS0Ru juliuskoskela@vega" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILnTMRhhsaZKKL1fwyXE6kRJkiTJwJxI4WoTAkUM99nV kisandst@kim-nvidia" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDJau0tg0qHhqFVarjNOJLi+ekSZNNqxal4iRD/pwM5W tervis@tervis-thinkpad" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAHVXc4s7e8j1uFsgHPBzpWvSI/hk5Zf6Btuj79D4hf3 tervis@tervis-servu" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM3w7NzqMuF+OAiIcYWyP9+J3kwvYMKQ+QeY9J8QjAXm shamma-alblooshi@tii.ae" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB/iv9RWMN6D9zmEU85XkaU8fAWJreWkv3znan87uqTW humaid@tahr" # For ghaf-installer automated testing: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAolaKCuIUBQSBFGFZI1taNX+JTAr8edqUts7A6k2Kv7" diff --git a/modules/reference/personalize/default.nix b/modules/reference/personalize/default.nix new file mode 100644 index 000000000..c967c24f3 --- /dev/null +++ b/modules/reference/personalize/default.nix @@ -0,0 +1,3 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ imports = [ ./keys.nix ]; } diff --git a/modules/reference/personalize/keys.nix b/modules/reference/personalize/keys.nix new file mode 100644 index 000000000..6058c2263 --- /dev/null +++ b/modules/reference/personalize/keys.nix @@ -0,0 +1,37 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.reference.personalize.keys; + inherit (lib) + mkEnableOption + mkIf + concatStrings + mkForce + ; + + authorizedYubikeys = [ + # Yubikey public keys for testing team, enabled only in debug mode + #1 + "ghaf:3HbulvTWYKkZEX6VaFX/EWLUp2FwHMUQQvhi8dGjOd1U+5gUxarLyqGcVzeAte5wpvTGkcRckcfN3Ce9iK0smA==,/j1T0Z4vNv72218WkRemtSMaqv4ysw6Oa6Db8KnLFczv5DxzBhHj+e3kinNX89wvwJWe9XlxPQqE54jmzi227w==,es256,+presence" + #2 + "ghaf:fkBGKisgW8B1AAQDe6l6QWMbvaM3vfIahYwnlWcyKoI0aM62hPBL3l1x5IUyQy41kpe1+nbR4K6KX43utDz7kA==,nEVF0RHTNpzRvem1Ng3KnHhlXXj28tvQvbA+YF39p6fzJpq0t9czGb85kmPms9pGquQiOFTDrEURUmdC6PA8Ng==,es256,+presence" + #3 + "ghaf:zQlVob4+w3DcvtN6BPjBPaEssJ3PYNSQVlWLk/Uq/Qlbqk9D0IjPjZDm5XwTuKhropVR1hVA4XdZKsSs9BlUEQ==,G3qgBAhmCwANuCdCZzo68QLFFQ4aud/a3X5r1m8UeUpMh5BlDHrHAR0sE0H/d4v7RiScex2TZaHrgYV507BFRA==,es256,+presence" + #4 + "ghaf:QaA1B4u1GzLt+HSwXpMxmdCOKiBN4WZSUAuEXZahNSpcv8xiYagp0ntVsl8TOx4K+sKls3gTn37Uso/dmncwdA==,mr0Nhwkok7VLUtkBMryOA0lZghU23SCYtU3CZeW5P4WVtnPax3N/6GkfuAv6Zw5ejC4BDvov3oKHTQT/F8eYqA==,es256,+presence" + ]; + + inherit ((import ./authorizedSshKeys.nix)) authorizedSshKeys; +in +{ + options.ghaf.reference.personalize.keys = { + enable = mkEnableOption "Enable personalization of keys for dev team"; + }; + + config = mkIf cfg.enable { + users.users.root.openssh.authorizedKeys.keys = authorizedSshKeys; + users.users.${config.ghaf.users.accounts.user}.openssh.authorizedKeys.keys = authorizedSshKeys; + ghaf.services.yubikey.u2fKeys = mkForce (concatStrings authorizedYubikeys); + }; +} diff --git a/modules/common/hardware/default.nix b/modules/reference/profiles/default.nix similarity index 56% rename from modules/common/hardware/default.nix rename to modules/reference/profiles/default.nix index c19ca9dd8..d2820a398 100644 --- a/modules/common/hardware/default.nix +++ b/modules/reference/profiles/default.nix @@ -1,11 +1,11 @@ # Copyright 2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 +# +# Ghaf Desktop Experience +# { imports = [ - ./x86_64-linux.nix - ./x86_64-generic - ./definition.nix - - ./ax88179_178a.nix + ./laptop-x86.nix + ./mvp-user-trial.nix ]; } diff --git a/modules/reference/profiles/laptop-x86.nix b/modules/reference/profiles/laptop-x86.nix new file mode 100644 index 000000000..5d838b9ed --- /dev/null +++ b/modules/reference/profiles/laptop-x86.nix @@ -0,0 +1,123 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.reference.profiles.laptop-x86; + listenerAddress = config.ghaf.logging.listener.address; + listenerPort = toString config.ghaf.logging.listener.port; +in +{ + imports = [ + ../../desktop/graphics + ../../common + ../../host + #TODO how to reference the miocrovm module here? + #self.nixosModules.microvm + #../microvm + ../../hardware/x86_64-generic + ../../hardware/common + ../../hardware/definition.nix + ../../lanzaboote + ]; + + options.ghaf.reference.profiles.laptop-x86 = { + enable = lib.mkEnableOption "Enable the basic x86 laptop config"; + + netvmExtraModules = lib.mkOption { + description = '' + List of additional modules to be passed to the netvm. + ''; + default = [ ]; + }; + + guivmExtraModules = lib.mkOption { + description = '' + List of additional modules to be passed to the guivm. + ''; + default = [ ]; + }; + + enabled-app-vms = lib.mkOption { + type = lib.types.listOf lib.types.attrs; + default = [ ]; + description = '' + List of appvms to include in the Ghaf reference appvms module + ''; + }; + }; + + config = lib.mkIf cfg.enable { + + ghaf = { + # Hardware definitions + hardware = { + x86_64.common.enable = true; + tpm2.enable = true; + usb.internal.enable = true; + usb.external.enable = true; + usb.vhotplug.enable = true; + }; + + # Virtualization options + virtualization = { + microvm-host = { + enable = true; + networkSupport = true; + }; + + microvm = { + netvm = { + enable = true; + wifi = true; + extraModules = cfg.netvmExtraModules; + }; + + adminvm = { + enable = true; + }; + + idsvm = { + enable = false; + mitmproxy.enable = false; + }; + + guivm = { + enable = true; + extraModules = cfg.guivmExtraModules; + }; + + audiovm = { + enable = true; + audio = true; + }; + + appvm = { + enable = true; + vms = cfg.enabled-app-vms; + }; + }; + }; + + # Enable givc + # @TODO change this flag to enable givc in release + givc.enable = config.ghaf.profiles.debug.enable; + + host = { + networking.enable = true; + }; + + # UI applications + # TODO fix this when defining desktop and apps + profiles = { + applications.enable = false; + }; + + # Logging configuration + logging.client.enable = true; + logging.client.endpoint = "http://${listenerAddress}:${listenerPort}/loki/api/v1/push"; + logging.listener.address = + "admin-vm" + lib.optionalString config.ghaf.profiles.debug.enable "-debug"; + logging.listener.port = 9999; + }; + }; +} diff --git a/modules/reference/profiles/mvp-user-trial.nix b/modules/reference/profiles/mvp-user-trial.nix new file mode 100644 index 000000000..a19200843 --- /dev/null +++ b/modules/reference/profiles/mvp-user-trial.nix @@ -0,0 +1,67 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.reference.profiles.mvp-user-trial; +in +{ + imports = [ + ../appvms + ../programs + ../services + ../personalize + ]; + + options.ghaf.reference.profiles.mvp-user-trial = { + enable = lib.mkEnableOption "Enable the mvp configuration for apps and services"; + }; + + config = lib.mkIf cfg.enable { + ghaf = { + reference = { + appvms = { + enable = true; + chromium-vm = true; + gala-vm = true; + zathura-vm = true; + comms-vm = true; + appflowy-vm = true; + business-vm = true; + }; + + services = { + enable = true; + dendrite = true; + }; + + programs = { + windows-launcher = { + enable = false; + spice = false; + }; + }; + + personalize = { + keys.enable = true; + }; + + profiles = { + laptop-x86 = { + enable = true; + netvmExtraModules = [ + ../services + ../personalize + { ghaf.reference.personalize.keys.enable = true; } + ]; + guivmExtraModules = [ + ../programs + ../personalize + { ghaf.reference.personalize.keys.enable = true; } + ]; + inherit (config.ghaf.reference.appvms) enabled-app-vms; + }; + }; + }; + }; + }; +} diff --git a/modules/reference/programs/chromium.nix b/modules/reference/programs/chromium.nix new file mode 100644 index 000000000..74c375d1c --- /dev/null +++ b/modules/reference/programs/chromium.nix @@ -0,0 +1,23 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.reference.programs.chromium; +in +{ + options.ghaf.reference.programs.chromium = { + enable = lib.mkEnableOption "Enable Chromium program settings"; + useZathuraVM = lib.mkEnableOption "Open PDFs in Zathura VM"; + }; + config = lib.mkIf cfg.enable { + programs.chromium = { + enable = true; + + # Fix border glitch when going maximised->minimised. + initialPrefs.browser.custom_chrome_frame = false; + + # Don't use pdf.js, open externally. + extraOpts."AlwaysOpenPdfExternally" = true; + }; + }; +} diff --git a/modules/reference/programs/default.nix b/modules/reference/programs/default.nix new file mode 100644 index 000000000..d10a53184 --- /dev/null +++ b/modules/reference/programs/default.nix @@ -0,0 +1,9 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + imports = [ + ./zathura.nix + ./chromium.nix + ./windows-launcher.nix + ]; +} diff --git a/modules/reference/programs/windows-launcher.nix b/modules/reference/programs/windows-launcher.nix new file mode 100644 index 000000000..d95f2b12f --- /dev/null +++ b/modules/reference/programs/windows-launcher.nix @@ -0,0 +1,47 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + pkgs, + config, + ... +}: +let + cfg = config.ghaf.reference.programs.windows-launcher; + windows-launcher = pkgs.callPackage ../../../packages/windows-launcher { enableSpice = cfg.spice; }; +in +{ + #TODO fix all these imports to correct scoping + imports = [ ../../desktop ]; + + options.ghaf.reference.programs.windows-launcher = { + enable = lib.mkEnableOption "Windows launcher"; + + spice = lib.mkEnableOption "remote access to the virtual machine using spice"; + + spice-port = lib.mkOption { + description = "Spice port"; + type = lib.types.int; + default = 5900; + }; + + spice-host = lib.mkOption { + description = "Spice host"; + type = lib.types.str; + default = "192.168.101.2"; + }; + }; + + config = lib.mkIf cfg.enable { + ghaf.graphics.launchers = lib.mkIf (!cfg.spice) [ + { + name = "Windows"; + path = "${windows-launcher}/bin/windows-launcher-ui"; + icon = "${pkgs.icon-pack}/distributor-logo-windows.svg"; + } + ]; + + networking.firewall.allowedTCPPorts = lib.mkIf cfg.spice [ cfg.spice-port ]; + environment.systemPackages = [ windows-launcher ]; + }; +} diff --git a/modules/reference/programs/zathura.nix b/modules/reference/programs/zathura.nix new file mode 100644 index 000000000..85b6b559f --- /dev/null +++ b/modules/reference/programs/zathura.nix @@ -0,0 +1,17 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + cfg = config.ghaf.reference.programs.zathura; +in +{ + options.ghaf.reference.programs.zathura = { + enable = lib.mkEnableOption "Enable Zathura program settings"; + }; + config = lib.mkIf cfg.enable { + # Use regular clipboard instead of primary clipboard. + environment.etc."zathurarc".text = '' + set selection-clipboard clipboard + ''; + }; +} diff --git a/modules/reference/services/default.nix b/modules/reference/services/default.nix new file mode 100644 index 000000000..2dbb1f824 --- /dev/null +++ b/modules/reference/services/default.nix @@ -0,0 +1,23 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +let + inherit (lib) mkEnableOption mkIf mkForce; + cfg = config.ghaf.reference.services; + isNetVM = "net-vm" == config.system.name; +in +{ + imports = [ + ./dendrite-pinecone/dendrite-pinecone.nix + ./dendrite-pinecone/dendrite-config.nix + ]; + options.ghaf.reference.services = { + enable = mkEnableOption "Enable the Ghaf reference services"; + dendrite = mkEnableOption "Enable the dendrite-pinecone service"; + }; + config = mkIf cfg.enable { + ghaf.reference.services = { + dendrite-pinecone.enable = mkForce (cfg.dendrite && isNetVM); + }; + }; +} diff --git a/modules/reference/services/dendrite-pinecone/dendrite-config.nix b/modules/reference/services/dendrite-pinecone/dendrite-config.nix new file mode 100644 index 000000000..4f8409015 --- /dev/null +++ b/modules/reference/services/dendrite-pinecone/dendrite-config.nix @@ -0,0 +1,34 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ config, lib, ... }: +{ + config.ghaf.reference.services.dendrite-pinecone = + let + externalNic = + let + firstPciWifiDevice = lib.head config.ghaf.hardware.definition.network.pciDevices; + in + "${firstPciWifiDevice.name}"; + + internalNic = + let + vmNetworking = import ../../../microvm/virtualization/microvm/common/vm-networking.nix { + inherit config; + inherit lib; + vmName = "net-vm"; + inherit (config.microvm.net-vm) macAddress; + internalIP = 1; + }; + in + "${lib.head vmNetworking.networking.nat.internalInterfaces}"; + + getCommsVmEntry = builtins.filter (x: x.name == "comms-vm") config.ghaf.networking.hosts.entries; + serverIpAddr = lib.head (builtins.map (x: x.ip) getCommsVmEntry); + in + { + enable = lib.mkDefault false; + inherit externalNic; + inherit internalNic; + inherit serverIpAddr; + }; +} diff --git a/modules/reference/services/dendrite-pinecone/dendrite-pinecone.nix b/modules/reference/services/dendrite-pinecone/dendrite-pinecone.nix new file mode 100644 index 000000000..b925f5165 --- /dev/null +++ b/modules/reference/services/dendrite-pinecone/dendrite-pinecone.nix @@ -0,0 +1,159 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.ghaf.reference.services.dendrite-pinecone; + dendrite-pineconePkg = pkgs.callPackage ../../../../packages/dendrite-pinecone/default.nix { }; + inherit (lib) + mkEnableOption + mkOption + mkIf + types + ; +in +{ + options.ghaf.reference.services.dendrite-pinecone = { + enable = mkEnableOption "Enable dendrite pinecone module"; + + externalNic = mkOption { + type = types.str; + default = ""; + description = '' + External network interface + ''; + }; + internalNic = mkOption { + type = types.str; + default = ""; + description = '' + Internal network interface + ''; + }; + + serverIpAddr = mkOption { + type = types.str; + default = ""; + description = '' + Dendrite Server Ip address + ''; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.externalNic != ""; + message = "External Nic must be set"; + } + { + assertion = cfg.internalNic != ""; + message = "Internal Nic must be set"; + } + { + assertion = cfg.serverIpAddr != ""; + message = "Dendrite Pinecone server ip must be set"; + } + ]; + + # ip forwarding functionality is needed for iptables + boot.kernel.sysctl."net.ipv4.ip_forward" = 1; + + # https://github.com/troglobit/smcroute?tab=readme-ov-file#linux-requirements + boot.kernelPatches = [ + { + name = "multicast-routing-config"; + patch = null; + extraStructuredConfig = with lib.kernel; { + IP_MULTICAST = yes; + IP_MROUTE = yes; + IP_PIMSM_V1 = yes; + IP_PIMSM_V2 = yes; + IP_MROUTE_MULTIPLE_TABLES = yes; # For multiple routing tables + }; + } + ]; + environment.systemPackages = [ pkgs.smcroute ]; + systemd.services."smcroute" = { + description = "Static Multicast Routing daemon"; + bindsTo = [ "sys-subsystem-net-devices-${cfg.externalNic}.device" ]; + after = [ "sys-subsystem-net-devices-${cfg.externalNic}.device" ]; + preStart = '' + configContent=$(cat < $filePath + chmod 400 $filePath + + # wait until ${cfg.externalNic} has an ip + while [ -z "$ip" ]; do + ip=$(${pkgs.nettools}/bin/ifconfig ${cfg.externalNic} | ${pkgs.gawk}/bin/awk '/inet / {print $2}') + [ -z "$ip" ] && ${pkgs.coreutils}/bin/sleep 1 + done + exit 0 + ''; + + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.smcroute}/sbin/smcrouted -n -s -f /etc/smcroute.conf"; + #TODO sudo setcap cap_net_admin=ep ${pkgs.smcroute}/sbin/smcroute + # TODO: Add proper AmbientCapabilities= or CapabilityBoundingSet=, + # preferably former and then change user to something else than + # root. + User = "root"; + # Automatically restart service when it exits. + Restart = "always"; + # Wait a second before restarting. + RestartSec = "5s"; + }; + wantedBy = [ "multi-user.target" ]; + }; + + networking = { + firewall.enable = true; + firewall.extraCommands = " + # Set the default policies + iptables -P INPUT DROP + iptables -P FORWARD DROP + iptables -P OUTPUT ACCEPT + + # Allow loopback traffic + iptables -A INPUT -i lo -j ACCEPT + + # TODO: Move all these TcpPort and things like that, to the options of + # this module, away from from package itself. + + # Forward incoming TCP traffic on port ${dendrite-pineconePkg.TcpPort} to internal network(comms-vm) + iptables -t nat -A PREROUTING -i ${cfg.externalNic} -p tcp --dport ${dendrite-pineconePkg.TcpPort} -j DNAT --to-destination ${cfg.serverIpAddr}:${dendrite-pineconePkg.TcpPort} + + # Enable NAT for outgoing traffic + iptables -t nat -A POSTROUTING -o ${cfg.externalNic} -p tcp --dport ${dendrite-pineconePkg.TcpPort} -j MASQUERADE + + # Enable NAT for outgoing traffic + iptables -t nat -A POSTROUTING -o ${cfg.externalNic} -p tcp --sport ${dendrite-pineconePkg.TcpPort} -j MASQUERADE + + # Enable NAT for outgoing udp multicast traffic + iptables -t nat -A POSTROUTING -o ${cfg.externalNic} -p udp -d ${dendrite-pineconePkg.McastUdpIp} --dport ${dendrite-pineconePkg.McastUdpPort} -j MASQUERADE + + # https://github.com/troglobit/smcroute?tab=readme-ov-file#usage + iptables -t mangle -I PREROUTING -i ${cfg.externalNic} -d ${dendrite-pineconePkg.McastUdpIp} -j TTL --ttl-set 1 + # ttl value must be set to 1 for avoiding multicast looping + iptables -t mangle -I PREROUTING -i ${cfg.internalNic} -d ${dendrite-pineconePkg.McastUdpIp} -j TTL --ttl-inc 1 + + # Accept forwarding + iptables -A FORWARD -j ACCEPT + "; + }; + }; +} diff --git a/nix/checks.nix b/nix/checks.nix index 47b2c062a..69167d988 100644 --- a/nix/checks.nix +++ b/nix/checks.nix @@ -1,28 +1,29 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{lib, ...}: { - perSystem = { - pkgs, - self', - ... - }: { - checks = - { - reuse = - pkgs.runCommandLocal "reuse-lint" { - buildInputs = [pkgs.reuse]; - } '' - cd ${../.} - reuse lint - touch $out - ''; - module-test-hardened-generic-host-kernel = - pkgs.callPackage ../modules/common/hardware/x86_64-generic/kernel/host/test {inherit pkgs;}; - module-test-hardened-lenovo-x1-guest-guivm-kernel = - pkgs.callPackage ../modules/common/hardware/lenovo-x1/kernel/guest/test {inherit pkgs;}; - module-test-hardened-pkvm-kernel = - pkgs.callPackage ../modules/common/hardware/x86_64-generic/kernel/host/pkvm/test {inherit pkgs;}; - } - // (lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages); - }; +{ + perSystem = + { + pkgs, + self', + lib, + ... + }: + { + checks = { + reuse = pkgs.runCommandLocal "reuse-lint" { buildInputs = [ pkgs.reuse ]; } '' + cd ${../.} + reuse lint + touch $out + ''; + #module-test-hardened-generic-host-kernel = + # pkgs.callPackage ../modules/hardware/x86_64-generic/kernel/host/test + # { inherit pkgs; }; + #module-test-hardened-lenovo-x1-guest-guivm-kernel = + # pkgs.callPackage ../modules/hardware/lenovo-x1/kernel/guest/test + # { inherit pkgs; }; + #module-test-hardened-pkvm-kernel = + # pkgs.callPackage ../modules/hardware/x86_64-generic/kernel/host/pkvm/test + # { inherit pkgs; }; + } // (lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages); + }; } diff --git a/nix/devshell.nix b/nix/devshell.nix index c3fe4bb32..06f864584 100644 --- a/nix/devshell.nix +++ b/nix/devshell.nix @@ -1,39 +1,47 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{inputs, ...}: { - imports = with inputs; [ - flake-root.flakeModule - ./devshell/kernel.nix - # TODO this import needs to be filtered to remove RISCV - # pre-commit-hooks-nix.flakeModule - ]; - perSystem = { - pkgs, - inputs', - self', - lib, - system, - ... - }: { - devShells.default = pkgs.mkShell { - name = "Ghaf devshell"; - #TODO look at adding Mission control etc here - packages = with pkgs; - [ - git - nix - nixci - nixos-rebuild - reuse - alejandra - mdbook - inputs'.nix-fast-build.packages.default - self'.packages.kernel-hardening-checker - ] - ++ lib.optional (pkgs.hostPlatform.system != "riscv64-linux") cachix; +{ + imports = [ ./devshell/kernel.nix ]; + perSystem = + { + config, + pkgs, + inputs', + lib, + ... + }: + { + devShells.default = pkgs.mkShell { + name = "Ghaf devshell"; + meta.description = "Ghaf development environment"; + #TODO look at adding Mission control etc here + inputsFrom = [ + config.treefmt.build.programs # See ./treefmt.nix + ]; + packages = + builtins.attrValues { + inherit (pkgs) + git + mdbook + nix + nixci + nixos-rebuild + nix-output-monitor + nix-tree + reuse + nix-eval-jobs + jq + ; + } + ++ [ inputs'.nix-fast-build.packages.default ] + ++ [ + (pkgs.callPackage ../packages/flash { }) + (pkgs.callPackage ../packages/make-checks { }) + ] + ++ lib.optional (pkgs.hostPlatform.system != "riscv64-linux") pkgs.cachix; - # TODO Add pre-commit.devShell (needs to exclude RiscV) - # https://flake.parts/options/pre-commit-hooks-nix + # TODO Add pre-commit.devShell (needs to exclude RiscV) + # https://flake.parts/options/pre-commit-hooks-nix + }; }; - }; } diff --git a/nix/devshell/kernel.nix b/nix/devshell/kernel.nix index 5cdc41249..111bc56c1 100644 --- a/nix/devshell/kernel.nix +++ b/nix/devshell/kernel.nix @@ -1,65 +1,68 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{inputs, ...}: { - perSystem = { - pkgs, - self', - system, - ... - }: let - mkKernelShell = { - platform, - arch ? "", - linux, - extraPackages ? [], - shellHook ? "", +{ inputs, ... }: +{ + perSystem = + { + pkgs, + self', + system, + ... }: - pkgs.mkShell { - name = "Kernel-${platform} devshell"; - packages = with pkgs; - [ - ncurses - pkg-config + let + mkKernelShell = + { + platform, + arch ? "", + linux, + extraPackages ? [ ], + shellHook ? "", + }: + pkgs.mkShell { + name = "Kernel-${platform} devshell"; + packages = [ + pkgs.ncurses + pkgs.pkg-config self'.packages.kernel-hardening-checker - ] - ++ extraPackages; + ] ++ extraPackages; - inputsFrom = [linux]; + inputsFrom = [ linux ]; - shellHook = '' - export src=${linux.src} - if [ -d "$src" ]; then - # Jetpack's kernel named "source-patched" or likewise, workaround it - linuxDir=$(stripHash ${linux.src}) - else - linuxDir="linux-${linux.version}" - fi - if [ ! -d "$linuxDir" ]; then - unpackPhase - patchPhase - fi - cd "$linuxDir" - # extra post-patching for NVidia - ${shellHook} + shellHook = '' + export src=${linux.src} + if [ -d "$src" ]; then + # Jetpack's kernel named "source-patched" or likewise, workaround it + linuxDir=$(stripHash ${linux.src}) + else + linuxDir="linux-${linux.version}" + fi + if [ ! -d "$linuxDir" ]; then + unpackPhase + patchPhase + fi + cd "$linuxDir" + # extra post-patching for NVidia + ${shellHook} - export PS1="[ghaf-kernel-${platform}-devshell:\w]$ " + export PS1="[ghaf-kernel-${platform}-devshell:\w]$ " + ''; + # use "eval $checkPhase" - see https://discourse.nixos.org/t/nix-develop-and-checkphase/25707 + checkPhase = "cp ../modules/hardware/${platform}/kernel/configs/ghaf_host_hardened_baseline-${arch} ./.config && make -j$(nproc)"; + }; + in + { + devShells.kernel-x86 = mkKernelShell { + platform = "x86_64-generic"; + arch = "x86"; + linux = pkgs.linux_latest; + }; + devShells.kernel-jetson-orin = mkKernelShell { + platform = "jetson-orin"; + linux = inputs.jetpack-nixos.legacyPackages.${system}.kernel; + extraPackages = [ pkgs.gawk ]; + shellHook = '' + patchShebangs scripts/ ''; - # use "eval $checkPhase" - see https://discourse.nixos.org/t/nix-develop-and-checkphase/25707 - checkPhase = "cp ../modules/common/hardware/${platform}/kernel/configs/ghaf_host_hardened_baseline-${arch} ./.config && make -j$(nproc)"; }; - in { - devShells.kernel-x86 = mkKernelShell { - platform = "x86_64-generic"; - arch = "x86"; - linux = pkgs.linux_latest; - }; - devShells.kernel-jetson-orin = mkKernelShell { - platform = "jetson-orin"; - linux = inputs.jetpack-nixos.legacyPackages.${system}.kernel; - extraPackages = [pkgs.gawk]; - shellHook = '' - patchShebangs scripts/ - ''; }; - }; } diff --git a/nix/nixpkgs.nix b/nix/nixpkgs.nix index e0e4f5392..fecabf1a0 100644 --- a/nix/nixpkgs.nix +++ b/nix/nixpkgs.nix @@ -1,19 +1,18 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 +{ lib, inputs, ... }: { - lib, - inputs, - ... -}: { - perSystem = {system, ...}: { - # customise pkgs - _module.args.pkgs = import inputs.nixpkgs { - inherit system inputs; - config = { - allowUnfree = true; + perSystem = + { system, ... }: + { + # customise pkgs + _module.args.pkgs = import inputs.nixpkgs { + inherit system inputs; + config = { + allowUnfree = true; + }; }; + # make custom top-level lib available to all `perSystem` functions + _module.args.lib = lib; }; - # make custom top-level lib available to all `perSystem` functions - _module.args.lib = lib; - }; } diff --git a/nix/treefmt.nix b/nix/treefmt.nix index c167a3b29..086b2cb3a 100644 --- a/nix/treefmt.nix +++ b/nix/treefmt.nix @@ -1,44 +1,75 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{inputs, ...}: { - imports = with inputs; [ - flake-root.flakeModule - treefmt-nix.flakeModule +{ inputs, ... }: +{ + imports = [ + inputs.flake-root.flakeModule + inputs.treefmt-nix.flakeModule + inputs.pre-commit-hooks-nix.flakeModule ]; - perSystem = { - config, - pkgs, - ... - }: { - treefmt.config = { - package = pkgs.treefmt; - inherit (config.flake-root) projectRootFile; + perSystem = + { config, pkgs, ... }: + { + treefmt.config = { + package = pkgs.treefmt; + inherit (config.flake-root) projectRootFile; - programs = { - # Nix - alejandra.enable = true; # nix formatter https://github.com/kamadorueda/alejandra - deadnix.enable = true; # removes dead nix code https://github.com/astro/deadnix - statix.enable = true; # prevents use of nix anti-patterns https://github.com/nerdypepper/statix + programs = { + # Nix + # nix standard formatter according to rfc 166 (https://github.com/NixOS/rfcs/pull/166) + nixfmt.enable = true; + nixfmt.package = pkgs.nixfmt-rfc-style; - # Python - # It was found out that the best outcome comes from running mulitple - # formatters. - black.enable = true; # The Classic Python formatter - isort.enable = true; # Python import sorter - # Ruff, a Python formatter written in Rust (30x faster than Black). - # Also provides additional linting. - # Do not enable ruff.format = true; because then it won't complaing - # about linting errors. The default mode is the check mode. - ruff.enable = true; + deadnix.enable = true; # removes dead nix code https://github.com/astro/deadnix + statix.enable = true; # prevents use of nix anti-patterns https://github.com/nerdypepper/statix - # Bash - shellcheck.enable = true; # lints shell scripts https://github.com/koalaman/shellcheck + # Python + # Ruff, a Python formatter and linter written in Rust (30x faster than Black). + ruff.check = true; + ruff.format = true; + + # Bash + shellcheck.enable = true; # lints shell scripts https://github.com/koalaman/shellcheck + + yamlfmt.enable = true; # YAML formatter + }; + + settings.global.excludes = [ + "*.key" + "*.lock" + "*.config" + "*.dts" + "*.pfx" + "*.p12" + "*.crt" + "*.cer" + "*.csr" + "*.der" + "*.jks" + "*.keystore" + "*.pem" + "*.pkcs12" + "*.pfx" + "*.p12" + "*.pem" + "*.pkcs7" + "*.p7b" + "*.p7c" + "*.p7r" + "*.p7m" + "*.p7s" + "*.p8" + "*.png" + "*.svg" + "*.license" + "*.db" + "*.mp3" + "*.txt" + #TODO: fix the MD + "*.md" + ]; }; - # Automatically fix linting errors and formatting errors where possible - settings.formatter.ruff.options = ["check" "--fix"]; + formatter = config.treefmt.build.wrapper; }; - - formatter = config.treefmt.build.wrapper; - }; } diff --git a/overlays/README.md b/overlays/README.md index eaf9fa8e1..1bc6e4e69 100644 --- a/overlays/README.md +++ b/overlays/README.md @@ -26,3 +26,21 @@ previous (unmodified) package vs final (finalazed, adjusted) package. Use deps[X][Y] variations instead of juggling dependencies between nativeBuildInputs and buildInputs where possible. It makes things clear and robust. + +# Upstream PR and commit tracking + +Some patches are carried as overlays and others are patches that are cherry-picked +from staging and main into a tiiuae maintained version of nixpkgs +[tiiuae/nixpkgs/...](https://github.com/tiiuae/nixpkgs/tree/patched-unstable-proc-qemu) + +The status of the integration in nixpkgs can be tracked using the [Pull Request Tracker](https://nixpk.gs/pr-tracker.html) + +## From Overlays + +[gtklock: Guard against race condition](https://github.com/jovanlanik/gtklock/pull/95) + +[gtklock: Fix black screen SP-4849](https://github.com/jovanlanik/gtklock/commit/e0e7f6d5ae7667fcc3479b6732046c67275b2f2f) + + +## carried in tiiuae/nixpkgs/... +[texinfo: cross compile failure](https://github.com/NixOS/nixpkgs/pull/328919) diff --git a/overlays/cross-compilation/chromium/default.nix b/overlays/cross-compilation/chromium/default.nix deleted file mode 100644 index 39ac10ab7..000000000 --- a/overlays/cross-compilation/chromium/default.nix +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2023-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# Chromium & Electron cross-compilation fixes -# -{ - final, - prev, -}: let - inherit (builtins) map; - inherit (final.lib) pipe; - opusWithCustomModes = final.pkgsBuildBuild.libopus.override { - withCustomModes = true; - }; - opusWithCustomModes' = final.pkgsBuildTarget.libopus.override { - withCustomModes = true; - }; - replace = needle: replacement: haystack: - map (each: - if each == needle - then replacement - else each) - haystack; -in - prev.chromium.overrideAttrs (oa: { - passthru = - oa.passthru - // { - mkDerivation = fun: - oa.passthru.mkDerivation (finalAttrs: - { - depsBuildBuild = pipe finalAttrs.depsBuildBuild [ - (replace (final.libpng.override {apngSupport = false;}) (final.pkgsBuildBuild.libpng.override {apngSupport = false;})) - (replace final.zlib final.pkgsBuildBuild.zlib) - (replace opusWithCustomModes opusWithCustomModes') - ]; - buildInputs = replace opusWithCustomModes' opusWithCustomModes finalAttrs.buildInputs; - env = finalAttrs.env // {NIX_DEBUG = "1";}; - } - // fun finalAttrs); - }; - }) diff --git a/overlays/cross-compilation/default.nix b/overlays/cross-compilation/default.nix index a7ec98939..d22e75137 100644 --- a/overlays/cross-compilation/default.nix +++ b/overlays/cross-compilation/default.nix @@ -3,14 +3,4 @@ # # This overlay is for specific fixes needed only to enable cross-compilation. # -(final: prev: { - chromium = import ./chromium {inherit prev final;}; - edk2 = import ./edk2 {inherit final prev;}; - element-desktop = import ./element-desktop {inherit prev;}; - jbig2dec = import ./jbig2dec {inherit prev;}; - pipewire = import ./pipewire {inherit prev;}; - - # libck is dependency of sysbench - libck = import ./libck {inherit prev;}; - sysbench = import ./sysbench {inherit final prev;}; -}) +(_final: prev: { papirus-icon-theme = import ./papirus-icon-theme { inherit prev; }; }) diff --git a/overlays/cross-compilation/edk2/default.nix b/overlays/cross-compilation/edk2/default.nix deleted file mode 100644 index 4a9d88a38..000000000 --- a/overlays/cross-compilation/edk2/default.nix +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2023-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# edk2 & OVMF cross-compilation fixes -# -{ - final, - prev, -}: -prev.edk2.overrideAttrs (oa: { - # Fix cross-compilation issue, use build cc/c++ for building antlr and dlg - postPatch = - (oa.postPatch or "") - + '' - substituteInPlace BaseTools/Source/C/VfrCompile/GNUmakefile \ - --replace '$(MAKE) -C Pccts/antlr' '$(MAKE) -C Pccts/antlr CC=cc CXX=c++' \ - --replace '$(MAKE) -C Pccts/dlg' '$(MAKE) -C Pccts/dlg CC=cc CXX=c++' - ''; - passthru = { - mkDerivation = dsc: fun: - oa.passthru.mkDerivation dsc (finalAttrs: - { - prePatch = '' - echo "prePatch hooked!" - rm -rf BaseTools - ln -sv ${final.buildPackages.edk2}/BaseTools BaseTools - ''; - - configurePhase = '' - echo "configurePhase hooked" - runHook preConfigure - export WORKSPACE="$PWD" - . ${final.buildPackages.edk2}/edksetup.sh BaseTools - runHook postConfigure - ''; - } - // fun finalAttrs); - }; -}) diff --git a/overlays/cross-compilation/element-desktop/default.nix b/overlays/cross-compilation/element-desktop/default.nix deleted file mode 100644 index ed37e040d..000000000 --- a/overlays/cross-compilation/element-desktop/default.nix +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# This overlay is for specific fixes needed only to enable cross-compilation. -# -# Overlay for element-desktop based on https://github.com/NixOS/nixpkgs/pull/241710 -{prev}: -(prev.element-desktop.override { - # Disable keytar, it breaks cross-build. Saving passwords would be not available. - useKeytar = false; -}) -.overrideAttrs (oldED: { - seshat = oldED.seshat.overrideAttrs (oldSeshat: { - buildPhase = - builtins.replaceStrings - # Add extra cargo options required for cross-compilation - ["build --release"] - ["build --release -- --target ${prev.rust.toRustTargetSpec prev.stdenv.hostPlatform} -Z unstable-options --out-dir target/release"] - # Replace target 'fixup_yarn_lock' with build one - (builtins.replaceStrings ["${prev.fixup_yarn_lock}"] ["${prev.buildPackages.fixup_yarn_lock}"] oldSeshat.buildPhase); - }); -}) diff --git a/overlays/cross-compilation/jbig2dec/default.nix b/overlays/cross-compilation/jbig2dec/default.nix deleted file mode 100644 index caf0d014c..000000000 --- a/overlays/cross-compilation/jbig2dec/default.nix +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright 2023-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{prev}: -prev.jbig2dec.overrideAttrs (_oa: { - configureScript = "./autogen.sh"; - preConfigure = ""; -}) diff --git a/overlays/cross-compilation/libck/default.nix b/overlays/cross-compilation/libck/default.nix deleted file mode 100644 index 0114e378b..000000000 --- a/overlays/cross-compilation/libck/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# libck cross-compilation fixes -# -{prev}: -prev.libck.overrideAttrs (_old: { - postPatch = '' - substituteInPlace \ - configure \ - --replace \ - 'COMPILER=`./.1 2> /dev/null`' \ - "COMPILER=gcc" - ''; - configureFlags = ["--platform=${prev.stdenv.hostPlatform.parsed.cpu.name}}"]; -}) diff --git a/overlays/cross-compilation/papirus-icon-theme/default.nix b/overlays/cross-compilation/papirus-icon-theme/default.nix new file mode 100644 index 000000000..a749efb0d --- /dev/null +++ b/overlays/cross-compilation/papirus-icon-theme/default.nix @@ -0,0 +1,9 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# papirus-icon-theme cross-compilation fixes (removing qt dependency) +# +{ prev }: +prev.papirus-icon-theme.overrideAttrs (old: { + propagatedBuildInputs = prev.lib.lists.remove prev.breeze-icons old.propagatedBuildInputs; +}) diff --git a/overlays/cross-compilation/pipewire/default.nix b/overlays/cross-compilation/pipewire/default.nix deleted file mode 100644 index f80093261..000000000 --- a/overlays/cross-compilation/pipewire/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright 2023-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{prev}: -# It defaulted to -# { ... -# , x11Support ? true -# , ffadoSupport ? x11Support && stdenv.buildPlatform.canExecute stdenv.hostPlatform -# } -# It should evaluate to `false` in case of cross-compilation, but it doesn't happens for unknown reasons. -prev.pipewire.override {ffadoSupport = false;} diff --git a/overlays/cross-compilation/sysbench/default.nix b/overlays/cross-compilation/sysbench/default.nix deleted file mode 100644 index 39ed5606d..000000000 --- a/overlays/cross-compilation/sysbench/default.nix +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# Sysbench cross-compilation fixes -# -{ - final, - prev, -}: -prev.sysbench.overrideAttrs (old: { - configureFlags = [ - "--with-system-luajit" - "--with-system-ck" - "--with-mysql-includes=${prev.lib.getDev final.libmysqlclient}/include/mysql" - "--with-mysql-libs=${final.libmysqlclient}/lib/mysql" - ]; - buildInputs = old.buildInputs ++ [final.libck]; - depsBuildBuild = [final.pkg-config]; -}) diff --git a/overlays/custom-packages/default.nix b/overlays/custom-packages/default.nix index 2d4f39484..82ef7845d 100644 --- a/overlays/custom-packages/default.nix +++ b/overlays/custom-packages/default.nix @@ -5,18 +5,18 @@ # packages. # (final: prev: { - gala-app = final.callPackage ../../packages/gala {}; - systemd = import ./systemd {inherit final prev;}; - waypipe = import ./waypipe {inherit final prev;}; - weston = import ./weston {inherit final prev;}; - wifi-connector = final.callPackage ../../packages/wifi-connector {}; - wifi-connector-nmcli = final.callPackage ../../packages/wifi-connector {useNmcli = true;}; - qemu_kvm = import ./qemu {inherit final prev;}; - nm-launcher = final.callPackage ../../packages/nm-launcher {}; - labwc = import ./labwc {inherit prev;}; - tpm2-pkcs11 = import ./tpm2-pkcs11 {inherit prev;}; - waybar = import ./waybar {inherit prev;}; - # launcher overlays - networkmanagerapplet = import ./networkmanagerapplet {inherit prev;}; - htop = import ./htop {inherit prev;}; + gala-app = final.callPackage ../../packages/gala { }; + element-desktop = import ./element-desktop { inherit prev; }; + element-gps = final.callPackage ../../packages/element-gps { }; + element-web = final.callPackage ../../packages/element-web { }; + waypipe = import ./waypipe { inherit final prev; }; + qemu_kvm = import ./qemu { inherit final prev; }; + nm-launcher = final.callPackage ../../packages/nm-launcher { }; + icon-pack = final.callPackage ../../packages/icon-pack { }; + labwc = import ./labwc { inherit prev; }; + tpm2-pkcs11 = import ./tpm2-pkcs11 { inherit prev; }; + waybar = import ./waybar { inherit prev; }; + mitmweb-ui = final.callPackage ../../packages/mitmweb-ui { }; + gtklock = import ./gtklock { inherit prev; }; + hardware-scan = final.callPackage ../../packages/hardware-scan { }; }) diff --git a/overlays/custom-packages/element-desktop/default.nix b/overlays/custom-packages/element-desktop/default.nix new file mode 100644 index 000000000..874543fd2 --- /dev/null +++ b/overlays/custom-packages/element-desktop/default.nix @@ -0,0 +1,9 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# This overlay customizes element-desktop +# +{ prev }: +prev.element-desktop.overrideAttrs (_prevAttrs: { + patches = [ ./element-main.patch ]; +}) diff --git a/overlays/custom-packages/element-desktop/element-main.patch b/overlays/custom-packages/element-desktop/element-main.patch new file mode 100644 index 000000000..22b9fa6da --- /dev/null +++ b/overlays/custom-packages/element-desktop/element-main.patch @@ -0,0 +1,91 @@ +diff --git a/src/electron-main.ts b/src/electron-main.ts +index b5d13ac..ffffb76 100644 +--- a/src/electron-main.ts ++++ b/src/electron-main.ts +@@ -458,11 +458,10 @@ + // https://www.electronjs.org/docs/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do + backgroundColor: "#fff", + +- titleBarStyle: process.platform === "darwin" ? "hidden" : "default", + trafficLightPosition: { x: 9, y: 8 }, + + icon: global.trayConfig.icon_path, +- show: false, ++ show: true, + autoHideMenuBar: global.store.get("autoHideMenuBar", true), + + x: mainWindowState.x, +@@ -477,67 +476,35 @@ + webgl: true, + }, + }); +- void global.mainWindow.loadURL("vector://vector/webapp/"); +- +- if (process.platform === "darwin") { +- setupMacosTitleBar(global.mainWindow); +- } + + // Handle spellchecker + // For some reason spellCheckerEnabled isn't persisted, so we have to use the store here + global.mainWindow.webContents.session.setSpellCheckerEnabled(global.store.get("spellCheckerEnabled", true)); + +- // Create trayIcon icon +- if (global.store.get("minimizeToTray", true)) tray.create(global.trayConfig); +- +- global.mainWindow.once("ready-to-show", () => { ++ global.mainWindow.webContents.once('did-finish-load',function(){ + if (!global.mainWindow) return; + mainWindowState.manage(global.mainWindow); + + if (!argv["hidden"]) { + global.mainWindow.show(); ++ global.mainWindow.restore(); ++ global.mainWindow.focus(); + } else { + // hide here explicitly because window manage above sometimes shows it + global.mainWindow.hide(); + } + }); + +- global.mainWindow.webContents.on("before-input-event", warnBeforeExit); ++ global.mainWindow.loadURL("vector://vector/webapp/"); + + global.mainWindow.on("closed", () => { + global.mainWindow = null; + }); + global.mainWindow.on("close", async (e) => { +- // If we are not quitting and have a tray icon then minimize to tray +- if (!global.appQuitting && (tray.hasTray() || process.platform === "darwin")) { +- // On Mac, closing the window just hides it +- // (this is generally how single-window Mac apps +- // behave, eg. Mail.app) +- e.preventDefault(); +- +- if (global.mainWindow?.isFullScreen()) { +- global.mainWindow.once("leave-full-screen", () => global.mainWindow?.hide()); +- +- global.mainWindow.setFullScreen(false); +- } else { +- global.mainWindow?.hide(); +- } +- +- return false; +- } ++ // Close event handler ++ // Default behaviour is minimize to tray, that feature is removed since there is no tray support on Ghaf + }); + +- if (process.platform === "win32") { +- // Handle forward/backward mouse buttons in Windows +- global.mainWindow.on("app-command", (e, cmd) => { +- if (cmd === "browser-backward" && global.mainWindow?.webContents.canGoBack()) { +- global.mainWindow.webContents.goBack(); +- } else if (cmd === "browser-forward" && global.mainWindow?.webContents.canGoForward()) { +- global.mainWindow.webContents.goForward(); +- } +- }); +- } +- + webContentsHandler(global.mainWindow.webContents); + + global.appLocalization = new AppLocalization({ diff --git a/overlays/custom-packages/element-gps/default.nix b/overlays/custom-packages/element-gps/default.nix new file mode 100644 index 000000000..94c81581e --- /dev/null +++ b/overlays/custom-packages/element-gps/default.nix @@ -0,0 +1,3 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +(final: _prev: { element-gps = final.callPackage ../../../packages/element-gps { }; }) diff --git a/overlays/custom-packages/element-web/default.nix b/overlays/custom-packages/element-web/default.nix new file mode 100644 index 000000000..fcfef04ca --- /dev/null +++ b/overlays/custom-packages/element-web/default.nix @@ -0,0 +1,3 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +(final: _prev: { element-web = final.callPackage ../../../packages/element-web { }; }) diff --git a/overlays/custom-packages/gtklock/auth-guard-against-race-condition-with-messages.patch b/overlays/custom-packages/gtklock/auth-guard-against-race-condition-with-messages.patch new file mode 100644 index 000000000..9d7b3e38f --- /dev/null +++ b/overlays/custom-packages/gtklock/auth-guard-against-race-condition-with-messages.patch @@ -0,0 +1,36 @@ +From d22127f0fd61bbeba8c12378b3c5b46cc3064d63 Mon Sep 17 00:00:00 2001 +From: Zephyr Lykos +Date: Sat, 22 Jun 2024 18:48:54 +0800 +Subject: [PATCH] auth: guard against race condition with messages + +--- + src/auth.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/auth.c b/src/auth.c +index a3a99b3..a5eb33d 100644 +--- a/src/auth.c ++++ b/src/auth.c +@@ -148,15 +148,15 @@ enum pwcheck auth_pw_check(const char *s) { + size_t len; + ssize_t nread; + nread = read(err_pipe[PIPE_PARENT], &len, sizeof(size_t)); +- if(nread > 0) { +- error_string = malloc(len+1); ++ if(nread > 0 && len <= PAM_MAX_MSG_SIZE) { ++ error_string = malloc(PAM_MAX_MSG_SIZE); + nread = read(err_pipe[PIPE_PARENT], error_string, len); + error_string[nread] = '\0'; + return PW_ERROR; + } + nread = read(out_pipe[PIPE_PARENT], &len, sizeof(size_t)); +- if(nread > 0) { +- message_string = malloc(len+1); ++ if(nread > 0 && len <= PAM_MAX_MSG_SIZE) { ++ message_string = malloc(PAM_MAX_MSG_SIZE); + nread = read(out_pipe[PIPE_PARENT], message_string, len); + message_string[nread] = '\0'; + return PW_MESSAGE; +-- +2.40.1 + diff --git a/overlays/custom-packages/gtklock/default.nix b/overlays/custom-packages/gtklock/default.nix new file mode 100644 index 000000000..aec6e2d05 --- /dev/null +++ b/overlays/custom-packages/gtklock/default.nix @@ -0,0 +1,15 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# This overlay will apply gtklock patches which are merged in master branch +# https://github.com/jovanlanik/gtklock/commit/d22127f0fd61bbeba8c12378b3c5b46cc3064d63 +# https://github.com/jovanlanik/gtklock/commit/e0e7f6d5ae7667fcc3479b6732046c67275b2f2f +# TODO: Remove patches, once there new release for gtlk-lock +# +{ prev }: +prev.gtklock.overrideAttrs { + patches = [ + ./auth-guard-against-race-condition-with-messages.patch + ./update.patch + ]; +} diff --git a/overlays/custom-packages/gtklock/update.patch b/overlays/custom-packages/gtklock/update.patch new file mode 100644 index 000000000..8dace56f6 --- /dev/null +++ b/overlays/custom-packages/gtklock/update.patch @@ -0,0 +1,116 @@ +From e0e7f6d5ae7667fcc3479b6732046c67275b2f2f Mon Sep 17 00:00:00 2001 +From: Jovan Lanik +Date: Tue, 16 Jul 2024 15:30:18 +0200 +Subject: [PATCH] update + +--- + include/window.h | 2 ++ + res/gtklock.ui | 20 +++++++++++++++++++- + src/window.c | 27 ++++++++++++++++++--------- + 3 files changed, 39 insertions(+), 10 deletions(-) + +diff --git a/include/window.h b/include/window.h +index 75b8372..77ba2c3 100644 +--- a/include/window.h ++++ b/include/window.h +@@ -17,6 +17,8 @@ struct Window { + GtkWidget *body_grid; + GtkWidget *input_label; + GtkWidget *input_field; ++ GtkWidget *message_revealer; ++ GtkWidget *message_scrolled_window; + GtkWidget *message_box; + GtkWidget *unlock_button; + GtkWidget *error_label; +diff --git a/res/gtklock.ui b/res/gtklock.ui +index 150a4d2..3aab413 100644 +--- a/res/gtklock.ui ++++ b/res/gtklock.ui +@@ -57,8 +57,26 @@ + + + +- ++ ++ none + 1 ++ ++ ++ never ++ 256 ++ 1 ++ ++ ++ ++ ++ vertical ++ 1 ++ ++ ++ ++ ++ ++ + + + 1 +diff --git a/src/window.c b/src/window.c +index a1a268b..d73eab0 100644 +--- a/src/window.c ++++ b/src/window.c +@@ -86,27 +86,34 @@ static GtkInfoBar *window_new_message(struct Window *ctx, char *msg) { + return GTK_INFO_BAR(bar); + } + ++static void destroy_callback(GtkWidget* widget, gpointer _data) { ++ gtk_widget_destroy(widget); ++} ++ + static void window_setup_messages(struct Window *ctx) { +- if(ctx->message_box != NULL) { +- gtk_widget_destroy(ctx->message_box); +- ctx->message_box = NULL; +- } +- ctx->message_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); +- gtk_widget_set_no_show_all(ctx->message_box, TRUE); +- gtk_grid_attach(GTK_GRID(ctx->body_grid), ctx->message_box, 1, 1, 2, 1); ++ gtk_container_foreach(GTK_CONTAINER(ctx->message_box), destroy_callback, NULL); ++ gtk_revealer_set_reveal_child(GTK_REVEALER(ctx->message_revealer), FALSE); ++ gtk_widget_hide(ctx->message_revealer); + + for(guint idx = 0; idx < gtklock->errors->len; idx++) { + char *err = g_array_index(gtklock->errors, char *, idx); + GtkInfoBar *bar = window_new_message(ctx, err); + gtk_info_bar_set_message_type(bar, GTK_MESSAGE_WARNING); +- gtk_widget_show(ctx->message_box); ++ ++ gtk_revealer_set_reveal_child(GTK_REVEALER(ctx->message_revealer), TRUE); ++ gtk_widget_show(ctx->message_revealer); ++ gtk_widget_show_all(ctx->message_scrolled_window); + } + for(guint idx = 0; idx < gtklock->messages->len; idx++) { + char *msg = g_array_index(gtklock->messages, char *, idx); + GtkInfoBar *bar = window_new_message(ctx, msg); + gtk_info_bar_set_message_type(bar, GTK_MESSAGE_INFO); +- gtk_widget_show(ctx->message_box); ++ ++ gtk_revealer_set_reveal_child(GTK_REVEALER(ctx->message_revealer), TRUE); ++ gtk_widget_show(ctx->message_revealer); ++ gtk_widget_show_all(ctx->message_scrolled_window); + } ++ + } + + static void window_set_busy(struct Window *ctx, gboolean busy) { +@@ -342,6 +349,8 @@ struct Window *create_window(GdkMonitor *monitor) { + w->input_field = GTK_WIDGET(gtk_builder_get_object(builder, "input-field")); + g_signal_connect(w->input_field, "button-press-event", G_CALLBACK(entry_button_press), NULL); + ++ w->message_revealer = GTK_WIDGET(gtk_builder_get_object(builder, "message-revealer")); ++ w->message_scrolled_window = GTK_WIDGET(gtk_builder_get_object(builder, "message-scrolled-window")); + w->message_box = GTK_WIDGET(gtk_builder_get_object(builder, "message-box")); + w->unlock_button = GTK_WIDGET(gtk_builder_get_object(builder, "unlock-button")); + w->error_label = GTK_WIDGET(gtk_builder_get_object(builder, "error-label")); +-- +2.40.1 + diff --git a/overlays/custom-packages/htop/default.nix b/overlays/custom-packages/htop/default.nix deleted file mode 100644 index 7fc7a7ed3..000000000 --- a/overlays/custom-packages/htop/default.nix +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# This overlay hides the desktop entry for htop -# -{prev}: -prev.htop.overrideAttrs { - postInstall = '' - echo "Hidden=true" >> $out/share/applications/htop.desktop - ''; -} diff --git a/overlays/custom-packages/labwc/default.nix b/overlays/custom-packages/labwc/default.nix index 51931b4f1..f67d47ad4 100644 --- a/overlays/custom-packages/labwc/default.nix +++ b/overlays/custom-packages/labwc/default.nix @@ -3,7 +3,4 @@ # # This overlay customizes labwc - see comments for details # -{prev}: -prev.labwc.overrideAttrs { - patches = [./labwc-colored-borders.patch]; -} +{ prev }: prev.labwc.overrideAttrs { patches = [ ./labwc-colored-borders.patch ]; } diff --git a/overlays/custom-packages/labwc/labwc-colored-borders.patch b/overlays/custom-packages/labwc/labwc-colored-borders.patch index 2fce4f393..833e4e719 100644 --- a/overlays/custom-packages/labwc/labwc-colored-borders.patch +++ b/overlays/custom-packages/labwc/labwc-colored-borders.patch @@ -1,49 +1,72 @@ -diff --git a/.gitignore b/.gitignore -new file mode 100644 -index 0000000..02f1769 ---- /dev/null -+++ b/.gitignore -@@ -0,0 +1,2 @@ -+result -+.vscode +From a89bc92a032d6c4fc50d711de92b068a0ed0a636 Mon Sep 17 00:00:00 2001 +From: Humaid Alqasimi +Date: Thu, 2 May 2024 10:02:56 +0400 +Subject: [PATCH] Add colored borders + +This allows to change the frame color of choosen app by using window +rules. As an example foot terminal has aqua colored frame. + +The patch approach was choosen cause there is no better solution +(which should revise the theme handling) ready. + +The discussion about better soultion will be held here: +labwc/labwc#1092. + +Co-authored-by: dmitry-erin +Signed-off-by: Humaid Alqasimi +--- + include/ssd-internal.h | 5 +++-- + include/theme.h | 10 ++++++++++ + include/window-rules.h | 17 +++++++++++++++++ + src/config/rcxml.c | 12 ++++++++++++ + src/ssd/ssd-border.c | 21 ++++++++++++++++----- + src/ssd/ssd-part.c | 2 +- + src/ssd/ssd-titlebar.c | 30 ++++++++++++++++++++++++++---- + src/ssd/ssd.c | 9 ++++++--- + src/theme.c | 34 ++++++++++++++++++++++++++++++++-- + src/window-rules.c | 25 +++++++++++++++++++++++++ + 10 files changed, 148 insertions(+), 17 deletions(-) + diff --git a/include/ssd-internal.h b/include/ssd-internal.h -index 9fe0ebf..6dfc2d4 100644 +index fda196e..b0e0cac 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h -@@ -118,8 +118,8 @@ struct ssd_part *add_scene_button( - struct wlr_buffer *icon_buffer, int x, struct view *view); +@@ -136,8 +136,9 @@ void add_toggled_icon(struct ssd_button *button, struct wl_list *part_list, struct ssd_part *add_scene_button_corner( struct wl_list *part_list, enum ssd_part_type type, -- enum ssd_part_type corner_type, struct wlr_scene_tree *parent, + enum ssd_part_type corner_type, struct wlr_scene_tree *parent, - struct wlr_buffer *corner_buffer, struct wlr_buffer *icon_buffer, -+ enum ssd_part_type corner_type, struct wlr_scene_tree *parent, -+ float *bg_color, struct wlr_buffer *corner_buffer, struct wlr_buffer *icon_buffer, - int x, struct view *view); +- struct wlr_buffer *hover_buffer, int x, struct view *view); ++ float *bg_color, struct wlr_buffer *corner_buffer, ++ struct wlr_buffer *icon_buffer, struct wlr_buffer *hover_buffer, ++ int x, struct view *view); /* SSD internal helpers */ + struct ssd_part *ssd_get_part( diff --git a/include/theme.h b/include/theme.h -index 47ef3b9..e0e5da4 100644 +index 50a69f6..7a0f976 100644 --- a/include/theme.h +++ b/include/theme.h -@@ -110,4 +110,13 @@ void theme_init(struct theme *theme, const char *theme_name); +@@ -162,4 +162,14 @@ void theme_init(struct theme *theme, struct server *server, const char *theme_na */ void theme_finish(struct theme *theme); +/** + * theme_customize_with_border_color - fill in the given theme color fields by custom color + * @theme: theme data ++ * @server: server + * @color: pointer to color array + */ -+void theme_customize_with_border_color(struct theme *theme, float *color); ++void theme_customize_with_border_color(struct theme *theme,struct server *server, float *color); + +void parse_hexstr(const char *hex, float *rgba); + #endif /* LABWC_THEME_H */ diff --git a/include/window-rules.h b/include/window-rules.h -index fae1daf..ff8163e 100644 +index b93bc36..066cc7f 100644 --- a/include/window-rules.h +++ b/include/window-rules.h -@@ -18,6 +18,7 @@ enum property { +@@ -21,6 +21,7 @@ enum property { * - 'app_id' for native Wayland windows * - 'WM_CLASS' for XWayland clients */ @@ -51,8 +74,8 @@ index fae1daf..ff8163e 100644 struct window_rule { char *identifier; char *title; -@@ -32,11 +33,27 @@ struct window_rule { - enum property ignore_focus_request; +@@ -40,11 +41,27 @@ struct window_rule { + enum property fixed_position; struct wl_list link; /* struct rcxml.window_rules */ + @@ -80,26 +103,26 @@ index fae1daf..ff8163e 100644 + #endif /* LABWC_WINDOW_RULES_H */ diff --git a/src/config/rcxml.c b/src/config/rcxml.c -index c8da660..57e40f0 100644 +index 84c117b..daab831 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c -@@ -29,6 +29,7 @@ - #include "regions.h" +@@ -35,6 +35,7 @@ + #include "view.h" #include "window-rules.h" #include "workspaces.h" +#include "theme.h" static bool in_regions; static bool in_usable_area_override; -@@ -112,6 +113,7 @@ fill_window_rule(char *nodename, char *content) - { +@@ -167,6 +168,7 @@ fill_window_rule(char *nodename, char *content) if (!strcasecmp(nodename, "windowRule.windowRules")) { current_window_rule = znew(*current_window_rule); + current_window_rule->window_type = -1; // Window types are >= 0 + init_window_rule(current_window_rule); wl_list_append(&rc.window_rules, ¤t_window_rule->link); wl_list_init(¤t_window_rule->actions); return; -@@ -127,6 +129,8 @@ fill_window_rule(char *nodename, char *content) +@@ -182,6 +184,8 @@ fill_window_rule(char *nodename, char *content) } else if (!strcmp(nodename, "identifier")) { free(current_window_rule->identifier); current_window_rule->identifier = xstrdup(content); @@ -108,45 +131,25 @@ index c8da660..57e40f0 100644 } else if (!strcmp(nodename, "title")) { free(current_window_rule->title); current_window_rule->title = xstrdup(content); -@@ -153,6 +157,14 @@ fill_window_rule(char *nodename, char *content) - } else if (!strcasecmp(nodename, "ignoreFocusRequest")) { - set_property(content, ¤t_window_rule->ignore_focus_request); +@@ -220,6 +224,14 @@ fill_window_rule(char *nodename, char *content) + } else if (!strcasecmp(nodename, "fixedPosition")) { + set_property(content, ¤t_window_rule->fixed_position); + /* Custom border properties: color */ + } else if (!strcasecmp(nodename, "borderColor")) { + parse_hexstr(content, current_window_rule->custom_border_color); + current_window_rule->has_custom_border = true; + wlr_log(WLR_DEBUG, "Custom borderColor was found in config: %s, parsed into: %f, %f, %f, %f\n", -+ content, current_window_rule->custom_border_color[0], current_window_rule->custom_border_color[1], ++ content, current_window_rule->custom_border_color[0], current_window_rule->custom_border_color[1], + current_window_rule->custom_border_color[2], current_window_rule->custom_border_color[3]); + /* Actions */ } else if (!strcmp(nodename, "name.action")) { current_window_rule_action = action_create(content); -diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c -index ef821b8..8fe1479 100644 ---- a/src/ssd/ssd.c -+++ b/src/ssd/ssd.c -@@ -333,9 +333,12 @@ ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable) - return; - } - -- float *color = enable -- ? rc.theme->window_toggled_keybinds_color -- : rc.theme->window_active_border_color; -+ float customColor[4]; -+ bool isCustomColorAvailable = window_rules_get_custom_border_color(ssd->view, customColor); -+ -+ float *color = isCustomColorAvailable ? customColor : -+ (enable ? rc.theme->window_toggled_keybinds_color -+ : rc.theme->window_active_border_color); - - struct ssd_part *part = ssd_get_part(&ssd->border.active.parts, LAB_SSD_PART_TOP); - struct wlr_scene_rect *rect = lab_wlr_scene_get_rect(part->node); -diff --git a/src/ssd/ssd_border.c b/src/ssd/ssd_border.c -index 6512ab8..9c042c6 100644 ---- a/src/ssd/ssd_border.c -+++ b/src/ssd/ssd_border.c +diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c +index 06ce55c..6e2fc06 100644 +--- a/src/ssd/ssd-border.c ++++ b/src/ssd/ssd-border.c @@ -6,6 +6,7 @@ #include "ssd-internal.h" #include "theme.h" @@ -183,40 +186,24 @@ index 6512ab8..9c042c6 100644 wl_list_init(&subtree->parts); add_scene_rect(&subtree->parts, LAB_SSD_PART_LEFT, parent, theme->border_width, height, 0, 0, color); -diff --git a/src/ssd/ssd_part.c b/src/ssd/ssd_part.c -index 4c72d29..9399435 100644 ---- a/src/ssd/ssd_part.c -+++ b/src/ssd/ssd_part.c -@@ -80,12 +80,11 @@ add_scene_buffer(struct wl_list *list, enum ssd_part_type type, +diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-part.c +index 3933cd1..8889c9e 100644 +--- a/src/ssd/ssd-part.c ++++ b/src/ssd/ssd-part.c +@@ -80,7 +80,7 @@ add_scene_buffer(struct wl_list *list, enum ssd_part_type type, struct ssd_part * add_scene_button_corner(struct wl_list *part_list, enum ssd_part_type type, - enum ssd_part_type corner_type, struct wlr_scene_tree *parent, + enum ssd_part_type corner_type, struct wlr_scene_tree *parent, float *bg_color, struct wlr_buffer *corner_buffer, struct wlr_buffer *icon_buffer, - int x, struct view *view) + struct wlr_buffer *hover_buffer, int x, struct view *view) { - int offset_x; -- float invisible[4] = { 0, 0, 0, 0 }; - - if (corner_type == LAB_SSD_PART_CORNER_TOP_LEFT) { - offset_x = rc.theme->border_width; -@@ -107,8 +106,8 @@ add_scene_button_corner(struct wl_list *part_list, enum ssd_part_type type, - add_scene_buffer(part_list, corner_type, parent, corner_buffer, - -offset_x, -rc.theme->border_width); - -- /* Finally just put a usual theme button on top, using an invisible hitbox */ -- add_scene_button(part_list, type, parent, invisible, icon_buffer, 0, view); -+ /* Finally just put a usual theme button on top, using an invisible/custom colored hitbox */ -+ add_scene_button(part_list, type, parent, bg_color, icon_buffer, 0, view); - return button_root; - } - -diff --git a/src/ssd/ssd_titlebar.c b/src/ssd/ssd_titlebar.c -index b0aaa2d..837bb4b 100644 ---- a/src/ssd/ssd_titlebar.c -+++ b/src/ssd/ssd_titlebar.c -@@ -24,6 +24,15 @@ ssd_titlebar_create(struct ssd *ssd) +diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c +index 32d6131..d183e52 100644 +--- a/src/ssd/ssd-titlebar.c ++++ b/src/ssd/ssd-titlebar.c +@@ -26,6 +26,15 @@ ssd_titlebar_create(struct ssd *ssd) { struct view *view = ssd->view; struct theme *theme = view->server->theme; @@ -225,51 +212,51 @@ index b0aaa2d..837bb4b 100644 + struct theme custom_theme = { 0 }; + float customColor[4]; + if (window_rules_get_custom_border_color(view, customColor)) { -+ theme_customize_with_border_color(&custom_theme, customColor); ++ theme_customize_with_border_color(&custom_theme, view->server, customColor); + theme = &custom_theme; + } + int width = view->current.width; float *color; -@@ -43,6 +52,7 @@ ssd_titlebar_create(struct ssd *ssd) +@@ -52,6 +61,7 @@ ssd_titlebar_create(struct ssd *ssd) subtree->tree = wlr_scene_tree_create(ssd->titlebar.tree); parent = subtree->tree; wlr_scene_node_set_position(&parent->node, 0, -theme->title_height); -+ ++ if (subtree == &ssd->titlebar.active) { color = theme->window_active_title_bg_color; corner_top_left = &theme->corner_top_left_active_normal->base; -@@ -62,6 +72,7 @@ ssd_titlebar_create(struct ssd *ssd) - close_button_unpressed = &theme->button_close_inactive_unpressed->base; +@@ -86,6 +96,7 @@ ssd_titlebar_create(struct ssd *ssd) + wlr_scene_node_set_enabled(&parent->node, false); } + wl_list_init(&subtree->parts); /* Title */ -@@ -71,7 +82,7 @@ ssd_titlebar_create(struct ssd *ssd) +@@ -95,7 +106,7 @@ ssd_titlebar_create(struct ssd *ssd) /* Buttons */ add_scene_button_corner(&subtree->parts, LAB_SSD_BUTTON_WINDOW_MENU, LAB_SSD_PART_CORNER_TOP_LEFT, parent, -- corner_top_left, menu_button_unpressed, 0, view); -+ color, corner_top_left, menu_button_unpressed, 0, view); +- corner_top_left, menu_button_unpressed, menu_button_hover, 0, view); ++ color, corner_top_left, menu_button_unpressed, menu_button_hover, 0, view); add_scene_button(&subtree->parts, LAB_SSD_BUTTON_ICONIFY, parent, - color, iconify_button_unpressed, + color, iconify_button_unpressed, iconify_button_hover, width - SSD_BUTTON_WIDTH * 3, view); -@@ -80,7 +91,7 @@ ssd_titlebar_create(struct ssd *ssd) - width - SSD_BUTTON_WIDTH * 2, view); +@@ -111,7 +122,7 @@ ssd_titlebar_create(struct ssd *ssd) + add_scene_button_corner(&subtree->parts, LAB_SSD_BUTTON_CLOSE, LAB_SSD_PART_CORNER_TOP_RIGHT, parent, -- corner_top_right, close_button_unpressed, -+ color, corner_top_right, close_button_unpressed, +- corner_top_right, close_button_unpressed, close_button_hover, ++ color, corner_top_right, close_button_unpressed, close_button_hover, width - SSD_BUTTON_WIDTH * 1, view); } FOR_EACH_END -@@ -111,10 +122,13 @@ set_squared_corners(struct ssd *ssd, bool enable) +@@ -149,10 +160,13 @@ set_squared_corners(struct ssd *ssd, bool enable) /* Toggle background between invisible and titlebar background color */ - struct wlr_scene_rect *rect = lab_wlr_scene_get_rect(button->background); + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(button->background); - wlr_scene_rect_set_color(rect, !enable ? (float[4]) {0, 0, 0, 0} : ( + /*Check for custom color as well*/ + float customColor[4]; @@ -282,11 +269,46 @@ index b0aaa2d..837bb4b 100644 /* Toggle rounded corner image itself */ struct wlr_scene_node *rounded_corner = +@@ -348,6 +362,14 @@ ssd_update_title(struct ssd *ssd) + } + + struct theme *theme = view->server->theme; ++ ++ /* Here the whole theme changing is more preferable */ ++ struct theme custom_theme = { 0 }; ++ float customColor[4]; ++ if (window_rules_get_custom_border_color(view, customColor)) { ++ theme_customize_with_border_color(&custom_theme, view->server, customColor); ++ theme = &custom_theme; ++ } + struct ssd_state_title *state = &ssd->state.title; + bool title_unchanged = state->text && !strcmp(title, state->text); + +diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c +index 70b1b0d..9dcc797 100644 +--- a/src/ssd/ssd.c ++++ b/src/ssd/ssd.c +@@ -411,9 +411,12 @@ ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable) + return; + } + +- float *color = enable +- ? rc.theme->window_toggled_keybinds_color +- : rc.theme->window_active_border_color; ++ float customColor[4]; ++ bool isCustomColorAvailable = window_rules_get_custom_border_color(ssd->view, customColor); ++ ++ float *color = isCustomColorAvailable ? customColor : ++ (enable ? rc.theme->window_toggled_keybinds_color ++ : rc.theme->window_active_border_color); + + struct ssd_part *part = ssd_get_part(&ssd->border.active.parts, LAB_SSD_PART_TOP); + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(part->node); diff --git a/src/theme.c b/src/theme.c -index 37dc803..d46e619 100644 +index 248a352..6f47bba 100644 --- a/src/theme.c +++ b/src/theme.c -@@ -168,7 +168,7 @@ hex_to_dec(char c) +@@ -401,7 +401,7 @@ hex_to_dec(char c) * @hex: hex string to be parsed * @rgba: pointer to float[4] for return value */ @@ -295,44 +317,41 @@ index 37dc803..d46e619 100644 parse_hexstr(const char *hex, float *rgba) { if (!hex || hex[0] != '#' || strlen(hex) < 7) { -@@ -211,7 +211,7 @@ parse_justification(const char *str) +@@ -470,7 +470,7 @@ parse_justification(const char *str) static void - theme_builtin(struct theme *theme) + theme_builtin(struct theme *theme, struct server *server) { - theme->border_width = 1; + theme->border_width = 5; theme->padding_height = 3; + theme->title_height = INT_MIN; theme->menu_overlap_x = 0; - theme->menu_overlap_y = 0; -@@ -807,7 +807,7 @@ theme_init(struct theme *theme, const char *theme_name) - theme_builtin(theme); - - /* Read /share/themes/$theme_name/openbox-3/themerc */ -- theme_read(theme, theme_name); -+ theme_read(theme, rc.theme_name); - - /* Read /labwc/themerc-override */ - theme_read_override(theme); -@@ -829,3 +829,27 @@ theme_finish(struct theme *theme) - theme->corner_top_right_active_normal = NULL; - theme->corner_top_right_inactive_normal = NULL; +@@ -1433,3 +1433,33 @@ theme_finish(struct theme *theme) + zdrop(&theme->shadow_corner_bottom_inactive); + zdrop(&theme->shadow_edge_inactive); } + -+void theme_customize_with_border_color(struct theme *theme, float *color) ++void theme_customize_with_border_color(struct theme *theme, struct server ++ *server, float *color) +{ -+ theme_builtin(theme); ++ theme_builtin(theme, server); + + /* Read /share/themes/$theme_name/openbox-3/themerc */ -+ theme_read(theme, rc.theme_name); ++ struct wl_list paths; ++ paths_theme_create(&paths, rc.theme_name, "themerc"); ++ theme_read(theme, &paths); ++ paths_destroy(&paths); + + /* Read /labwc/themerc-override */ -+ theme_read_override(theme); -+ ++ paths_config_create(&paths, "themerc-override"); ++ theme_read(theme, &paths); ++ paths_destroy(&paths); ++ + memcpy(theme->window_active_border_color, color, sizeof(float)*4); + memcpy(theme->window_inactive_border_color, color, sizeof(float)*4); + memcpy(theme->window_active_title_bg_color, color, sizeof(float)*4); + memcpy(theme->window_inactive_title_bg_color, color, sizeof(float)*4); -+ ++ + memcpy(theme->osd_bg_color, color, sizeof(float)*4); + memcpy(theme->osd_border_color, color, sizeof(float)*4); + memcpy(theme->window_toggled_keybinds_color, color, sizeof(float)*4); @@ -341,13 +360,12 @@ index 37dc803..d46e619 100644 + create_corners(theme); + load_buttons(theme); +} -\ No newline at end of file diff --git a/src/window-rules.c b/src/window-rules.c -index 2607199..7fc8d34 100644 +index f543f7e..5ea5d53 100644 --- a/src/window-rules.c +++ b/src/window-rules.c -@@ -74,6 +74,14 @@ view_matches_criteria(struct window_rule *rule, struct view *view) - } +@@ -43,6 +43,14 @@ view_matches_criteria(struct window_rule *rule, struct view *view) + return view_matches_query(view, &query); } +void @@ -361,7 +379,7 @@ index 2607199..7fc8d34 100644 void window_rules_apply(struct view *view, enum window_rule_event event) { -@@ -132,3 +140,20 @@ window_rules_get_property(struct view *view, const char *property) +@@ -109,3 +117,20 @@ window_rules_get_property(struct view *view, const char *property) } return LAB_PROP_UNSPECIFIED; } @@ -382,3 +400,6 @@ index 2607199..7fc8d34 100644 + + return false; +} +-- +2.44.1 + diff --git a/overlays/custom-packages/mitmweb-ui/default.nix b/overlays/custom-packages/mitmweb-ui/default.nix new file mode 100644 index 000000000..a0994ce8f --- /dev/null +++ b/overlays/custom-packages/mitmweb-ui/default.nix @@ -0,0 +1,3 @@ +# Copyright 2022-2023 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +(final: _prev: { mitmweb-ui = final.callPackage ../../../packages/mitmweb-ui { }; }) diff --git a/overlays/custom-packages/networkmanagerapplet/default.nix b/overlays/custom-packages/networkmanagerapplet/default.nix deleted file mode 100644 index fb840b72b..000000000 --- a/overlays/custom-packages/networkmanagerapplet/default.nix +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# This overlay hides the desktop entry for network settings -# -{prev}: -prev.networkmanagerapplet.overrideAttrs { - postInstall = '' - echo "Hidden=true" >> $out/share/applications/nm-connection-editor.desktop - ''; -} diff --git a/overlays/custom-packages/qemu/default.nix b/overlays/custom-packages/qemu/default.nix index 43492a877..ec0008eda 100644 --- a/overlays/custom-packages/qemu/default.nix +++ b/overlays/custom-packages/qemu/default.nix @@ -1,19 +1,17 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - final, - prev, -}: let +{ final, prev }: +let qemu_version = prev.qemu_kvm.version; qemu_major = final.lib.versions.major qemu_version; qemu_minor = final.lib.versions.minor qemu_version; in - prev.qemu_kvm.overrideAttrs ( - _final: prev: - (final.lib.optionalAttrs (qemu_major == "8" && qemu_minor == "0") { - patches = prev.patches ++ [./acpi-devices-passthrough-qemu-8.0.patch]; - }) - // (final.lib.optionalAttrs (qemu_major == "8" && qemu_minor == "1") { - patches = prev.patches ++ [./acpi-devices-passthrough-qemu-8.1.patch]; - }) - ) +prev.qemu_kvm.overrideAttrs ( + _final: prev: + (final.lib.optionalAttrs (qemu_major == "8" && qemu_minor == "0") { + patches = prev.patches ++ [ ./acpi-devices-passthrough-qemu-8.0.patch ]; + }) + // (final.lib.optionalAttrs (final.lib.versionAtLeast qemu_version "8.1") { + patches = prev.patches ++ [ ./acpi-devices-passthrough-qemu-8.1.patch ]; + }) +) diff --git a/overlays/custom-packages/systemd/default.nix b/overlays/custom-packages/systemd/default.nix deleted file mode 100644 index 846879293..000000000 --- a/overlays/custom-packages/systemd/default.nix +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - final, - prev, -}: let - # The patch has been added nixpkgs upstream, don't override attributes if - # the patch is already present. - # - # https://github.com/NixOS/nixpkgs/pull/239201 - shouldOverride = !(final.lib.lists.any (p: final.lib.strings.hasSuffix "timesyncd-disable-NSCD-when-DNSSEC-validation-is-dis.patch" (toString p)) prev.systemd.patches); -in - prev.systemd.overrideAttrs (prevAttrs: - final.lib.optionalAttrs shouldOverride { - patches = prevAttrs.patches ++ [./systemd-timesyncd-disable-nscd.patch]; - postPatch = - prevAttrs.postPatch - + '' - substituteInPlace units/systemd-timesyncd.service.in \ - --replace \ - "Environment=SYSTEMD_NSS_RESOLVE_VALIDATE=0" \ - "${final.lib.concatStringsSep "\n" [ - "Environment=LD_LIBRARY_PATH=$out/lib" - "Environment=SYSTEMD_NSS_RESOLVE_VALIDATE=0" - ]}" - ''; - }) diff --git a/overlays/custom-packages/systemd/systemd-timesyncd-disable-nscd.patch b/overlays/custom-packages/systemd/systemd-timesyncd-disable-nscd.patch deleted file mode 100644 index 3b22174f8..000000000 --- a/overlays/custom-packages/systemd/systemd-timesyncd-disable-nscd.patch +++ /dev/null @@ -1,46 +0,0 @@ -From bb150a178bebb88ce3bfe8c726c75d495423b4a2 Mon Sep 17 00:00:00 2001 -From: Yuri Nesterov -Date: Wed, 21 Jun 2023 17:17:38 +0300 -Subject: [PATCH] timesyncd: disable NSCD when DNSSEC validation is disabled - -Systemd-timesyncd sets SYSTEMD_NSS_RESOLVE_VALIDATE=0 in the unit file -to disable DNSSEC validation but it doesn't work when NSCD is used in -the system. This patch disabes NSCD in systemd-timesyncd when -SYSTEMD_NSS_RESOLVE_VALIDATE is set to 0 so that it uses NSS libraries -directly. ---- - src/timesync/timesyncd.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c -index 1d8ebecc91..2b0ae361ff 100644 ---- a/src/timesync/timesyncd.c -+++ b/src/timesync/timesyncd.c -@@ -21,6 +21,11 @@ - #include "timesyncd-conf.h" - #include "timesyncd-manager.h" - #include "user-util.h" -+#include "env-util.h" -+ -+struct traced_file; -+extern void __nss_disable_nscd(void (*)(size_t, struct traced_file *)); -+static void register_traced_file(size_t dbidx, struct traced_file *finfo) {} - - static int advance_tstamp(int fd, const struct stat *st) { - assert_se(fd >= 0); -@@ -198,6 +203,12 @@ static int run(int argc, char *argv[]) { - if (r < 0) - return log_error_errno(r, "Failed to parse fallback server strings: %m"); - -+ r = getenv_bool_secure("SYSTEMD_NSS_RESOLVE_VALIDATE"); -+ if (r == 0) { -+ log_info("Disabling NSCD because DNSSEC validation is turned off"); -+ __nss_disable_nscd(register_traced_file); -+ } -+ - log_debug("systemd-timesyncd running as pid " PID_FMT, getpid_cached()); - - notify_message = notify_start("READY=1\n" --- -2.34.1 - diff --git a/overlays/custom-packages/tpm2-pkcs11/default.nix b/overlays/custom-packages/tpm2-pkcs11/default.nix index af36ef686..003024b1d 100644 --- a/overlays/custom-packages/tpm2-pkcs11/default.nix +++ b/overlays/custom-packages/tpm2-pkcs11/default.nix @@ -3,7 +3,7 @@ # # This overlay customizes tpm2-pkcs11 - see comments for details # -{prev}: +{ prev }: prev.tpm2-pkcs11.overrideAttrs (_prevAttrs: { - configureFlags = ["--with-fapi=no --enable-fapi=no"]; + configureFlags = [ "--with-fapi=no --enable-fapi=no" ]; }) diff --git a/overlays/custom-packages/waybar/default.nix b/overlays/custom-packages/waybar/default.nix index f303a54f4..d4445aded 100644 --- a/overlays/custom-packages/waybar/default.nix +++ b/overlays/custom-packages/waybar/default.nix @@ -3,11 +3,11 @@ # # This overlay customizes waybar # -{prev}: ( - prev.waybar.override { - hyprlandSupport = false; - swaySupport = false; - jackSupport = false; - cavaSupport = false; - } -) +{ prev }: +(prev.waybar.override { + hyprlandSupport = false; + swaySupport = false; + jackSupport = false; + cavaSupport = false; + pulseSupport = false; +}) diff --git a/overlays/custom-packages/waypipe/default.nix b/overlays/custom-packages/waypipe/default.nix index 526cd3eed..1fec6db3f 100644 --- a/overlays/custom-packages/waypipe/default.nix +++ b/overlays/custom-packages/waypipe/default.nix @@ -1,17 +1,8 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - final, - prev, -}: +{ prev, ... }: # Waypipe with vsock and window borders prev.waypipe.overrideAttrs (_prevAttrs: { - src = final.pkgs.fetchFromGitLab { - domain = "gitlab.freedesktop.org"; - owner = "mstoeckl"; - repo = "waypipe"; - rev = "ca4809435e781dfc6bd3006fde605860c8dcf179"; - sha256 = "sha256-tSLPlf7fVq8vwbr7fHotqM/sBSXYMDM1V5yth5bhi38="; - }; - patches = [./waypipe-window-borders.patch]; + # Upstream pull request: https://gitlab.freedesktop.org/mstoeckl/waypipe/-/merge_requests/21 + patches = [ ./waypipe-window-borders.patch ]; }) diff --git a/overlays/custom-packages/waypipe/waypipe-window-borders.patch b/overlays/custom-packages/waypipe/waypipe-window-borders.patch index c2968a309..31d781dc2 100644 --- a/overlays/custom-packages/waypipe/waypipe-window-borders.patch +++ b/overlays/custom-packages/waypipe/waypipe-window-borders.patch @@ -1,34 +1,63 @@ -From 134f22a39a360ebf9cd9f118766edb7df925b732 Mon Sep 17 00:00:00 2001 +From b993dca0e0919cf16c207026605f0fe5a61f479f Mon Sep 17 00:00:00 2001 From: Yuri Nesterov -Date: Tue, 10 Oct 2023 14:57:03 +0300 +Date: Fri, 24 May 2024 11:15:41 +0200 Subject: [PATCH] Add support for coloured window borders +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +This is usefor to visually distinguish between different windows when +using waypipe. The border is drawn around the window and can be +configured with a hex color and a border size in pixels. + +Signed-off-by: Jörg Thalheim --- - protocols/function_list.txt | 3 + - src/handlers.c | 112 ++++++++++++++++++++++++++++++++++++ + protocols/function_list.txt | 4 ++ + src/handlers.c | 121 ++++++++++++++++++++++++++++++++++++ src/main.h | 3 + src/parsing.h | 4 ++ - src/util.c | 14 ++++- + src/util.c | 12 ++++ src/util.h | 6 ++ - src/waypipe.c | 68 +++++++++++++++++++++- - 7 files changed, 207 insertions(+), 3 deletions(-) + src/waypipe.c | 70 ++++++++++++++++++++- + waypipe.scd | 5 ++ + 8 files changed, 223 insertions(+), 2 deletions(-) diff --git a/protocols/function_list.txt b/protocols/function_list.txt -index 300408d..eb2d8b5 100644 +index 4acaec5..4750263 100644 --- a/protocols/function_list.txt +++ b/protocols/function_list.txt -@@ -49,3 +49,6 @@ zwp_linux_dmabuf_v1_req_get_default_feedback - zwp_linux_dmabuf_v1_req_get_surface_feedback - zwp_primary_selection_offer_v1_req_receive - zwp_primary_selection_source_v1_evt_send -+xdg_wm_base_req_get_xdg_surface -+xdg_surface_req_set_window_geometry +@@ -16,6 +16,7 @@ wl_registry_req_bind + wl_shm_req_create_pool + wl_shm_pool_req_create_buffer + wl_shm_pool_req_resize ++wl_surface_evt_preferred_buffer_scale + wl_surface_req_attach + wl_surface_req_commit + wl_surface_req_damage +@@ -25,7 +26,10 @@ wl_surface_req_set_buffer_scale + wp_presentation_evt_clock_id + wp_presentation_feedback_evt_presented + wp_presentation_req_feedback +xdg_surface_req_get_toplevel ++xdg_surface_req_set_window_geometry + xdg_toplevel_req_set_title ++xdg_wm_base_req_get_xdg_surface + zwlr_data_control_offer_v1_req_receive + zwlr_data_control_source_v1_evt_send + zwlr_export_dmabuf_frame_v1_evt_frame diff --git a/src/handlers.c b/src/handlers.c -index db08ee5..fd4650e 100644 +index c82f4e0..50ff7a3 100644 --- a/src/handlers.c +++ b/src/handlers.c -@@ -345,6 +345,13 @@ struct wp_object *create_wp_object(uint32_t id, const struct wp_interface *type) +@@ -98,6 +98,7 @@ struct obj_wl_surface { + uint32_t attached_buffer_id; /* protocol object id */ + int32_t scale; + int32_t transform; ++ int32_t preferred_buffer_scale; + }; + + struct obj_wlr_screencopy_frame { +@@ -357,6 +358,13 @@ struct wp_object *create_wp_object(uint32_t id, const struct wp_interface *type) } else if (type == &intf_wl_surface) { ((struct obj_wl_surface *)new_obj)->scale = 1; } @@ -42,7 +71,7 @@ index db08ee5..fd4650e 100644 return new_obj; } -@@ -730,6 +737,87 @@ static void rotate_damage_lists(struct obj_wl_surface *surface) +@@ -743,6 +751,88 @@ static void rotate_damage_lists(struct obj_wl_surface *surface) (SURFACE_DAMAGE_BACKLOG - 1) * sizeof(uint64_t)); surface->attached_buffer_uids[0] = 0; } @@ -108,16 +137,17 @@ index db08ee5..fd4650e 100644 + + if ((buf->shm_format != WL_SHM_FORMAT_ARGB8888) && (buf->shm_format != WL_SHM_FORMAT_XRGB8888)) { + wp_debug("Unable to draw the border, SHM format %d is not supported", buf->shm_format); -+ } -+ else { ++ } else { + if (ctx->obj->xdg_surface_id) { + struct wp_object *xdg_surface = tracker_get(ctx->tracker, ctx->obj->xdg_surface_id); + if (xdg_surface && xdg_surface->is_window) { -+ int32_t x1 = xdg_surface->window_x; -+ int32_t y1 = xdg_surface->window_y; -+ int32_t x2 = min(buf->shm_width, xdg_surface->window_x + xdg_surface->window_width); -+ int32_t y2 = min(buf->shm_height, xdg_surface->window_y + xdg_surface->window_height); ++ int32_t scale = surface->preferred_buffer_scale > 0 ? surface->preferred_buffer_scale : 1; ++ int32_t x1 = xdg_surface->window_x * scale; ++ int32_t y1 = xdg_surface->window_y * scale; ++ int32_t x2 = min(buf->shm_width, (xdg_surface->window_x + xdg_surface->window_width) * scale); ++ int32_t y2 = min(buf->shm_height, (xdg_surface->window_y + xdg_surface->window_height) * scale); + int32_t border_size = min(min(ctx->g->config->border_size, x2 - x1), y2 - y1); ++ + draw_rect(buf, x1, y1, x2, y1 + border_size, &ctx->g->config->border_color); // top + draw_rect(buf, x1, y1 + border_size, x1 + border_size, y2, &ctx->g->config->border_color); // left + draw_rect(buf, x1 + border_size, y2 - border_size, x2, y2, &ctx->g->config->border_color); // bottom @@ -130,7 +160,7 @@ index db08ee5..fd4650e 100644 void do_wl_surface_req_commit(struct context *ctx) { struct obj_wl_surface *surface = (struct obj_wl_surface *)ctx->obj; -@@ -747,6 +835,10 @@ void do_wl_surface_req_commit(struct context *ctx) +@@ -760,6 +850,10 @@ void do_wl_surface_req_commit(struct context *ctx) /* commit signifies a client-side update only */ return; } @@ -141,7 +171,21 @@ index db08ee5..fd4650e 100644 struct wp_object *obj = tracker_get(ctx->tracker, surface->attached_buffer_id); if (!obj) { -@@ -1976,3 +2068,23 @@ void do_zwlr_gamma_control_v1_req_set_gamma(struct context *ctx, int fd) +@@ -921,6 +1015,13 @@ static void append_damage_record(struct obj_wl_surface *surface, int32_t x, + damage->width = width; + damage->height = height; + } ++ ++void do_wl_surface_evt_preferred_buffer_scale(struct context *ctx, int32_t scale) ++{ ++ struct obj_wl_surface *surface = (struct obj_wl_surface *)ctx->obj; ++ surface->preferred_buffer_scale = scale; ++} ++ + void do_wl_surface_req_damage(struct context *ctx, int32_t x, int32_t y, + int32_t width, int32_t height) + { +@@ -2021,3 +2122,23 @@ void do_xdg_toplevel_req_set_title(struct context *ctx, const char *str) } const struct wp_interface *the_display_interface = &intf_wl_display; @@ -166,13 +210,13 @@ index db08ee5..fd4650e 100644 + ctx->obj->window_height = height; +} diff --git a/src/main.h b/src/main.h -index cf260b0..75e0a27 100644 +index 48ddae8..919b069 100644 --- a/src/main.h +++ b/src/main.h -@@ -45,6 +45,9 @@ struct main_config { - uint32_t vsock_cid; +@@ -46,6 +46,9 @@ struct main_config { uint32_t vsock_port; bool vsock_to_host; + const char *title_prefix; + bool border; + struct color border_color; + uint32_t border_size; @@ -195,16 +239,13 @@ index f3580b0..5739001 100644 struct message_tracker { /* Tree containing all objects that are currently alive or zombie */ diff --git a/src/util.c b/src/util.c -index d43aa17..6aade78 100644 +index 8b4bce9..c4ff390 100644 --- a/src/util.c +++ b/src/util.c -@@ -739,4 +739,16 @@ int listen_on_vsock(uint32_t port, int nmaxclients, int *socket_fd_out) - *socket_fd_out = sock; +@@ -794,3 +794,15 @@ int listen_on_vsock(uint32_t port, int nmaxclients, int *socket_fd_out) return 0; } --#endif -\ No newline at end of file -+#endif + #endif + +uint8_t hex_char_to_int(uint8_t hex) +{ @@ -218,10 +259,10 @@ index d43aa17..6aade78 100644 + return 0; +} diff --git a/src/util.h b/src/util.h -index 81cb2a8..780bff2 100644 +index 9970840..8e5cec1 100644 --- a/src/util.h +++ b/src/util.h -@@ -514,4 +514,10 @@ int connect_to_vsock(uint32_t port, uint32_t cid, bool to_host, int *socket_fd); +@@ -517,4 +517,10 @@ int connect_to_vsock(uint32_t port, uint32_t cid, bool to_host, int *socket_fd); int listen_on_vsock(uint32_t port, int nmaxclients, int *socket_fd_out); #endif @@ -233,10 +274,18 @@ index 81cb2a8..780bff2 100644 + #endif // WAYPIPE_UTIL_H diff --git a/src/waypipe.c b/src/waypipe.c -index 1c1be71..61e2200 100644 +index c66a971..0dbec96 100644 --- a/src/waypipe.c +++ b/src/waypipe.c -@@ -399,6 +399,53 @@ static int parse_vsock_addr(const char *str, struct main_config *config) +@@ -86,6 +86,7 @@ static const char usage_string[] = + " vsock: [[s]CID:]port\n" + " --version print waypipe version and exit\n" + " --allow-tiled allow gpu buffers (DMABUFs) with format modifiers\n" ++ " --border C,S server: add a border with hex color C and border size S in hex around the window\n" + " --control C server,ssh: set control pipe to reconnect server\n" + " --display D server,ssh: the Wayland display name or path\n" + " --drm-node R set the local render node. default: /dev/dri/renderD128\n" +@@ -400,6 +401,53 @@ static int parse_vsock_addr(const char *str, struct main_config *config) } #endif @@ -290,20 +339,19 @@ index 1c1be71..61e2200 100644 static const char *feature_names[] = { "lz4", "zstd", -@@ -448,6 +495,7 @@ static const bool feature_flags[] = { - #define ARG_WAYPIPE_BINARY 1011 +@@ -450,6 +498,7 @@ static const bool feature_flags[] = { #define ARG_BENCH_TEST_SIZE 1012 #define ARG_VSOCK 1013 -+#define ARG_BORDER 1014 + #define ARG_TITLE_PREFIX 1014 ++#define ARG_BORDER 1015 static const struct option options[] = { {"compress", required_argument, NULL, 'c'}, -@@ -469,7 +517,11 @@ static const struct option options[] = { - {"display", required_argument, NULL, ARG_DISPLAY}, - {"control", required_argument, NULL, ARG_CONTROL}, +@@ -473,7 +522,10 @@ static const struct option options[] = { {"test-size", required_argument, NULL, ARG_BENCH_TEST_SIZE}, -- {"vsock", no_argument, NULL, ARG_VSOCK}, {0, 0, NULL, 0}}; -+ {"vsock", no_argument, NULL, ARG_VSOCK}, + {"vsock", no_argument, NULL, ARG_VSOCK}, + {"title-prefix", required_argument, NULL, ARG_TITLE_PREFIX}, +- {0, 0, NULL, 0}}; + {"border", required_argument, NULL, ARG_BORDER}, + {0, 0, NULL, 0} +}; @@ -311,41 +359,59 @@ index 1c1be71..61e2200 100644 struct arg_permissions { int val; uint32_t mode_mask; -@@ -497,6 +549,7 @@ static const struct arg_permissions arg_permissions[] = { +@@ -498,7 +550,9 @@ static const struct arg_permissions arg_permissions[] = { {ARG_CONTROL, MODE_SSH | MODE_SERVER}, {ARG_BENCH_TEST_SIZE, MODE_BENCH}, {ARG_VSOCK, MODE_SSH | MODE_CLIENT | MODE_SERVER}, -+ {ARG_BORDER, MODE_SERVER}, - }; +- {ARG_TITLE_PREFIX, MODE_SSH | MODE_CLIENT | MODE_SERVER}}; ++ {ARG_TITLE_PREFIX, MODE_SSH | MODE_CLIENT | MODE_SERVER}, ++ {ARG_BORDER, MODE_SERVER}, ++}; /* envp is nonstandard, so use environ */ -@@ -533,7 +586,12 @@ int main(int argc, char **argv) - .vsock = false, - .vsock_cid = 2, /* VMADDR_CID_HOST */ + extern char **environ; +@@ -541,6 +595,11 @@ int main(int argc, char **argv) .vsock_to_host = false, /* VMADDR_FLAG_TO_HOST */ -- .vsock_port = 0}; -+ .vsock_port = 0, + .vsock_port = 0, + .title_prefix = NULL, + .border = false, + .border_color = { + .a = 255, .r = 0, .g = 0, .b = 0 + }, -+ .border_size = 5}; ++ .border_size = 3 + }; /* We do not parse any getopt arguments happening after the mode choice - * string, so as not to interfere with them. */ -@@ -705,6 +763,12 @@ int main(int argc, char **argv) - fprintf(stderr, "Option --vsock not allowed: this copy of Waypipe was not built with support for Linux VM sockets.\n"); - return EXIT_FAILURE; - #endif -+ case ARG_BORDER: { +@@ -724,6 +783,13 @@ int main(int argc, char **argv) + } + config.title_prefix = optarg; + break; ++ case ARG_BORDER: + config.border = true; + if (parse_border(optarg, &config) == -1) { -+ fail = true; ++ fprintf(stderr, "Invalid border argument: %s\n", optarg); ++ return EXIT_FAILURE; + } -+ } break; ++ break; default: fail = true; break; +diff --git a/waypipe.scd b/waypipe.scd +index d0b300d..f555b30 100644 +--- a/waypipe.scd ++++ b/waypipe.scd +@@ -111,6 +111,11 @@ compressible as images containing pictures. + absolute path, the socket will be created in the folder given by the + environment variable _XDG_RUNTIME_DIR_.) + ++*--border C,S* ++ For server: add a border with hex color C and border size S in hex around the ++ window. The hex color should be in the format #RRGGBB or #RRGGBBAA and ++ the border size is in pixels. ++ + *--drm-node R* + Specify the path *R* to the drm device that this instance of waypipe should + use and (in server mode) notify connecting applications about. -- -2.34.1 +2.45.1 diff --git a/overlays/custom-packages/weston/default.nix b/overlays/custom-packages/weston/default.nix deleted file mode 100644 index d65439756..000000000 --- a/overlays/custom-packages/weston/default.nix +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# This overlay customizes weston - see comments for details -# -{ - final, - prev, -}: -# First, weston package is overridden -( - prev.weston.override { - freerdp = null; - pipewire = null; - pipewireSupport = false; - rdpSupport = false; - vncSupport = false; - xwayland = null; - xwaylandSupport = false; - } -) -# and then this overridden package's attributes are overridden -.overrideAttrs ( - _prevAttrs: - # TODO: Add patch for 13.0 which is coming in NixOS 24.05 - final.lib.optionalAttrs ((final.lib.versions.majorMinor prev.weston.version) == "12.0") { - patches = [./weston-backport-workspaces.patch]; - } -) diff --git a/overlays/custom-packages/weston/weston-backport-workspaces.patch b/overlays/custom-packages/weston/weston-backport-workspaces.patch deleted file mode 100644 index 9ab9cb5e1..000000000 --- a/overlays/custom-packages/weston/weston-backport-workspaces.patch +++ /dev/null @@ -1,807 +0,0 @@ -commit 5aaed50e2cd1635084325ff5fe5fad2bd72354cf -Author: Yuri Nesterov -Date: Thu Oct 5 15:56:08 2023 +0300 - - Revert "desktop-shell: Remove multiple workspace support" - - This reverts commit 61d8238874d9c0ad6530c6826209f87c332627eb. - -diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c -index e4ea90f9..e57f5ccd 100644 ---- a/desktop-shell/shell.c -+++ b/desktop-shell/shell.c -@@ -127,6 +127,8 @@ struct shell_surface { - struct weston_curtain *black_view; - } fullscreen; - -+ struct weston_transform workspace_transform; -+ - struct weston_output *fullscreen_output; - struct weston_output *output; - struct wl_listener output_destroy_listener; -@@ -495,6 +497,9 @@ shell_configuration(struct desktop_shell *shell) - weston_config_section_get_string(section, "focus-animation", &s, "none"); - shell->focus_animation_type = get_animation_type(s); - free(s); -+ weston_config_section_get_uint(section, "num-workspaces", -+ &shell->workspaces.num, -+ DEFAULT_NUM_WORKSPACES); - } - - static int -@@ -510,6 +515,15 @@ focus_surface_committed(struct weston_surface *es, int32_t sx, int32_t sy) - { - } - -+static struct focus_surface * -+get_focus_surface(struct weston_surface *surface) -+{ -+ if (surface->committed == focus_surface_committed) -+ return surface->committed_private; -+ else -+ return NULL; -+} -+ - static bool - is_focus_view (struct weston_view *view) - { -@@ -541,6 +555,8 @@ create_focus_surface(struct weston_compositor *ec, - weston_view_set_output(fsurf->curtain->view, output); - fsurf->curtain->view->is_mapped = true; - -+ wl_list_init(&fsurf->workspace_transform.link); -+ - return fsurf; - } - -@@ -733,6 +749,21 @@ restore_focus_state(struct desktop_shell *shell, struct workspace *ws) - } - } - -+static void -+replace_focus_state(struct desktop_shell *shell, struct workspace *ws, -+ struct weston_seat *seat) -+{ -+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); -+ struct focus_state *state; -+ -+ wl_list_for_each(state, &ws->focus_list, link) { -+ if (state->seat == seat) { -+ focus_state_set_focus(state, keyboard->focus); -+ return; -+ } -+ } -+} -+ - static void - drop_focus_state(struct desktop_shell *shell, struct workspace *ws, - struct weston_surface *surface) -@@ -826,6 +857,7 @@ workspace_destroy(struct workspace *ws) - focus_surface_destroy(ws->fsurf_back); - - desktop_shell_destroy_layer(&ws->layer); -+ free(ws); - } - - static void -@@ -842,13 +874,14 @@ seat_destroyed(struct wl_listener *listener, void *data) - wl_list_remove(&state->link); - } - --static void -+static struct workspace * - workspace_create(struct desktop_shell *shell) - { -- struct workspace *ws = &shell->workspace; -+ struct workspace *ws = malloc(sizeof *ws); -+ if (ws == NULL) -+ return NULL; - - weston_layer_init(&ws->layer, shell->compositor); -- weston_layer_set_position(&ws->layer, WESTON_LAYER_POSITION_NORMAL); - - wl_list_init(&ws->focus_list); - wl_list_init(&ws->seat_destroyed_listener.link); -@@ -856,12 +889,343 @@ workspace_create(struct desktop_shell *shell) - ws->fsurf_front = NULL; - ws->fsurf_back = NULL; - ws->focus_animation = NULL; -+ -+ return ws; -+} -+ -+static int -+workspace_is_empty(struct workspace *ws) -+{ -+ return wl_list_empty(&ws->layer.view_list.link); -+} -+ -+static struct workspace * -+get_workspace(struct desktop_shell *shell, unsigned int index) -+{ -+ struct workspace **pws = shell->workspaces.array.data; -+ assert(index < shell->workspaces.num); -+ pws += index; -+ return *pws; - } - - struct workspace * - get_current_workspace(struct desktop_shell *shell) - { -- return &shell->workspace; -+ return get_workspace(shell, shell->workspaces.current); -+} -+ -+static void -+activate_workspace(struct desktop_shell *shell, unsigned int index) -+{ -+ struct workspace *ws; -+ -+ ws = get_workspace(shell, index); -+ weston_layer_set_position(&ws->layer, WESTON_LAYER_POSITION_NORMAL); -+ -+ shell->workspaces.current = index; -+} -+ -+static unsigned int -+get_output_height(struct weston_output *output) -+{ -+ return abs(output->region.extents.y1 - output->region.extents.y2); -+} -+ -+static struct weston_transform * -+view_get_transform(struct weston_view *view) -+{ -+ struct focus_surface *fsurf = NULL; -+ struct shell_surface *shsurf = NULL; -+ -+ if (is_focus_view(view)) { -+ fsurf = get_focus_surface(view->surface); -+ return &fsurf->workspace_transform; -+ } -+ -+ shsurf = get_shell_surface(view->surface); -+ if (shsurf) -+ return &shsurf->workspace_transform; -+ -+ return NULL; -+} -+ -+static void -+view_translate(struct workspace *ws, struct weston_view *view, double d) -+{ -+ struct weston_transform *transform = view_get_transform(view); -+ -+ if (!transform) -+ return; -+ -+ if (wl_list_empty(&transform->link)) -+ wl_list_insert(view->geometry.transformation_list.prev, -+ &transform->link); -+ -+ weston_matrix_init(&transform->matrix); -+ weston_matrix_translate(&transform->matrix, -+ 0.0, d, 0.0); -+ weston_view_geometry_dirty(view); -+} -+ -+static void -+workspace_translate_out(struct workspace *ws, double fraction) -+{ -+ struct weston_view *view; -+ unsigned int height; -+ double d; -+ -+ wl_list_for_each(view, &ws->layer.view_list.link, layer_link.link) { -+ height = get_output_height(view->surface->output); -+ d = height * fraction; -+ -+ view_translate(ws, view, d); -+ } -+} -+ -+static void -+workspace_translate_in(struct workspace *ws, double fraction) -+{ -+ struct weston_view *view; -+ unsigned int height; -+ double d; -+ -+ wl_list_for_each(view, &ws->layer.view_list.link, layer_link.link) { -+ height = get_output_height(view->surface->output); -+ -+ if (fraction > 0) -+ d = -(height - height * fraction); -+ else -+ d = height + height * fraction; -+ -+ view_translate(ws, view, d); -+ } -+} -+ -+static void -+reverse_workspace_change_animation(struct desktop_shell *shell, -+ unsigned int index, -+ struct workspace *from, -+ struct workspace *to) -+{ -+ shell->workspaces.current = index; -+ -+ shell->workspaces.anim_to = to; -+ shell->workspaces.anim_from = from; -+ shell->workspaces.anim_dir = -1 * shell->workspaces.anim_dir; -+ shell->workspaces.anim_timestamp = (struct timespec) { 0 }; -+ -+ weston_layer_set_position(&to->layer, WESTON_LAYER_POSITION_NORMAL); -+ weston_layer_set_position(&from->layer, WESTON_LAYER_POSITION_NORMAL - 1); -+ -+ weston_compositor_schedule_repaint(shell->compositor); -+} -+ -+static void -+workspace_deactivate_transforms(struct workspace *ws) -+{ -+ struct weston_view *view; -+ struct weston_transform *transform; -+ -+ wl_list_for_each(view, &ws->layer.view_list.link, layer_link.link) { -+ transform = view_get_transform(view); -+ if (!transform) -+ continue; -+ -+ if (!wl_list_empty(&transform->link)) { -+ wl_list_remove(&transform->link); -+ wl_list_init(&transform->link); -+ } -+ weston_view_geometry_dirty(view); -+ } -+} -+ -+static void -+finish_workspace_change_animation(struct desktop_shell *shell, -+ struct workspace *from, -+ struct workspace *to) -+{ -+ struct weston_view *view; -+ -+ weston_compositor_schedule_repaint(shell->compositor); -+ -+ /* Views that extend past the bottom of the output are still -+ * visible after the workspace animation ends but before its layer -+ * is hidden. In that case, we need to damage below those views so -+ * that the screen is properly repainted. */ -+ wl_list_for_each(view, &from->layer.view_list.link, layer_link.link) -+ weston_view_damage_below(view); -+ -+ wl_list_remove(&shell->workspaces.animation.link); -+ workspace_deactivate_transforms(from); -+ workspace_deactivate_transforms(to); -+ shell->workspaces.anim_to = NULL; -+ -+ weston_layer_unset_position(&shell->workspaces.anim_from->layer); -+} -+ -+static void -+animate_workspace_change_frame(struct weston_animation *animation, -+ struct weston_output *output, -+ const struct timespec *time) -+{ -+ struct desktop_shell *shell = -+ container_of(animation, struct desktop_shell, -+ workspaces.animation); -+ struct workspace *from = shell->workspaces.anim_from; -+ struct workspace *to = shell->workspaces.anim_to; -+ int64_t t; -+ double x, y; -+ -+ if (workspace_is_empty(from) && workspace_is_empty(to)) { -+ finish_workspace_change_animation(shell, from, to); -+ return; -+ } -+ -+ if (timespec_is_zero(&shell->workspaces.anim_timestamp)) { -+ if (shell->workspaces.anim_current == 0.0) -+ shell->workspaces.anim_timestamp = *time; -+ else -+ timespec_add_msec(&shell->workspaces.anim_timestamp, -+ time, -+ /* Inverse of movement function 'y' below. */ -+ -(asin(1.0 - shell->workspaces.anim_current) * -+ DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH * -+ M_2_PI)); -+ } -+ -+ t = timespec_sub_to_msec(time, &shell->workspaces.anim_timestamp); -+ -+ /* -+ * x = [0, π/2] -+ * y(x) = sin(x) -+ */ -+ x = t * (1.0/DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH) * M_PI_2; -+ y = sin(x); -+ -+ if (t < DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH) { -+ weston_compositor_schedule_repaint(shell->compositor); -+ -+ workspace_translate_out(from, shell->workspaces.anim_dir * y); -+ workspace_translate_in(to, shell->workspaces.anim_dir * y); -+ shell->workspaces.anim_current = y; -+ -+ weston_compositor_schedule_repaint(shell->compositor); -+ } -+ else -+ finish_workspace_change_animation(shell, from, to); -+} -+ -+static void -+animate_workspace_change(struct desktop_shell *shell, -+ unsigned int index, -+ struct workspace *from, -+ struct workspace *to) -+{ -+ struct weston_output *output; -+ -+ int dir; -+ -+ if (index > shell->workspaces.current) -+ dir = -1; -+ else -+ dir = 1; -+ -+ shell->workspaces.current = index; -+ -+ shell->workspaces.anim_dir = dir; -+ shell->workspaces.anim_from = from; -+ shell->workspaces.anim_to = to; -+ shell->workspaces.anim_current = 0.0; -+ shell->workspaces.anim_timestamp = (struct timespec) { 0 }; -+ -+ output = container_of(shell->compositor->output_list.next, -+ struct weston_output, link); -+ wl_list_insert(&output->animation_list, -+ &shell->workspaces.animation.link); -+ -+ weston_layer_set_position(&to->layer, WESTON_LAYER_POSITION_NORMAL); -+ weston_layer_set_position(&from->layer, WESTON_LAYER_POSITION_NORMAL - 1); -+ -+ workspace_translate_in(to, 0); -+ -+ restore_focus_state(shell, to); -+ -+ weston_compositor_schedule_repaint(shell->compositor); -+} -+ -+static void -+update_workspace(struct desktop_shell *shell, unsigned int index, -+ struct workspace *from, struct workspace *to) -+{ -+ shell->workspaces.current = index; -+ weston_layer_set_position(&to->layer, WESTON_LAYER_POSITION_NORMAL); -+ weston_layer_unset_position(&from->layer); -+} -+ -+static void -+change_workspace(struct desktop_shell *shell, unsigned int index) -+{ -+ struct workspace *from; -+ struct workspace *to; -+ struct focus_state *state; -+ -+ if (index == shell->workspaces.current) -+ return; -+ -+ /* Don't change workspace when there is any fullscreen surfaces. */ -+ if (!wl_list_empty(&shell->fullscreen_layer.view_list.link)) -+ return; -+ -+ from = get_current_workspace(shell); -+ to = get_workspace(shell, index); -+ -+ if (shell->workspaces.anim_from == to && -+ shell->workspaces.anim_to == from) { -+ restore_focus_state(shell, to); -+ reverse_workspace_change_animation(shell, index, from, to); -+ return; -+ } -+ -+ if (shell->workspaces.anim_to != NULL) -+ finish_workspace_change_animation(shell, -+ shell->workspaces.anim_from, -+ shell->workspaces.anim_to); -+ -+ restore_focus_state(shell, to); -+ -+ if (shell->focus_animation_type != ANIMATION_NONE) { -+ wl_list_for_each(state, &from->focus_list, link) -+ if (state->keyboard_focus) -+ animate_focus_change(shell, from, -+ get_default_view(state->keyboard_focus), NULL); -+ -+ wl_list_for_each(state, &to->focus_list, link) -+ if (state->keyboard_focus) -+ animate_focus_change(shell, to, -+ NULL, get_default_view(state->keyboard_focus)); -+ } -+ -+ if (workspace_is_empty(to) && workspace_is_empty(from)) -+ update_workspace(shell, index, from, to); -+ else -+ animate_workspace_change(shell, index, from, to); -+} -+ -+static bool -+workspace_has_only(struct workspace *ws, struct weston_surface *surface) -+{ -+ struct wl_list *list = &ws->layer.view_list.link; -+ struct wl_list *e; -+ -+ if (wl_list_empty(list)) -+ return false; -+ -+ e = list->next; -+ -+ if (e->next != list) -+ return false; -+ -+ return container_of(e, struct weston_view, layer_link.link)->surface == surface; - } - - static void -@@ -884,6 +1248,68 @@ surface_keyboard_focus_lost(struct weston_surface *surface) - } - } - -+static void -+take_surface_to_workspace_by_seat(struct desktop_shell *shell, -+ struct weston_seat *seat, -+ unsigned int index) -+{ -+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); -+ struct weston_surface *surface; -+ struct weston_view *view; -+ struct shell_surface *shsurf; -+ struct workspace *from; -+ struct workspace *to; -+ struct focus_state *state; -+ -+ surface = weston_surface_get_main_surface(keyboard->focus); -+ view = get_default_view(surface); -+ if (view == NULL || -+ index == shell->workspaces.current || -+ is_focus_view(view)) -+ return; -+ -+ from = get_current_workspace(shell); -+ to = get_workspace(shell, index); -+ -+ weston_layer_entry_remove(&view->layer_link); -+ weston_layer_entry_insert(&to->layer.view_list, &view->layer_link); -+ -+ shsurf = get_shell_surface(surface); -+ if (shsurf != NULL) -+ shell_surface_update_child_surface_layers(shsurf); -+ -+ replace_focus_state(shell, to, seat); -+ drop_focus_state(shell, from, surface); -+ -+ if (shell->workspaces.anim_from == to && -+ shell->workspaces.anim_to == from) { -+ reverse_workspace_change_animation(shell, index, from, to); -+ -+ return; -+ } -+ -+ if (shell->workspaces.anim_to != NULL) -+ finish_workspace_change_animation(shell, -+ shell->workspaces.anim_from, -+ shell->workspaces.anim_to); -+ -+ if (workspace_is_empty(from) && -+ workspace_has_only(to, surface)) -+ update_workspace(shell, index, from, to); -+ else { -+ if (shsurf != NULL && -+ wl_list_empty(&shsurf->workspace_transform.link)) -+ wl_list_insert(&shell->workspaces.anim_sticky_list, -+ &shsurf->workspace_transform.link); -+ -+ animate_workspace_change(shell, index, from, to); -+ } -+ -+ state = ensure_focus_state(shell, seat); -+ if (state != NULL) -+ focus_state_set_focus(state, surface); -+} -+ - static void - touch_move_grab_down(struct weston_touch_grab *grab, - const struct timespec *time, -@@ -1875,6 +2301,8 @@ desktop_surface_added(struct weston_desktop_surface *desktop_surface, - wl_list_init(&shsurf->rotation.transform.link); - weston_matrix_init(&shsurf->rotation.rotation); - -+ wl_list_init(&shsurf->workspace_transform.link); -+ - /* - * initialize list as well as link. The latter allows to use - * wl_list_remove() even when this surface is not in another list. -@@ -4234,6 +4662,86 @@ force_kill_binding(struct weston_keyboard *keyboard, - kill(pid, SIGKILL); - } - -+static void -+workspace_up_binding(struct weston_keyboard *keyboard, -+ const struct timespec *time, uint32_t key, void *data) -+{ -+ struct desktop_shell *shell = data; -+ unsigned int new_index = shell->workspaces.current; -+ -+ if (shell->locked) -+ return; -+ if (new_index != 0) -+ new_index--; -+ -+ change_workspace(shell, new_index); -+} -+ -+static void -+workspace_down_binding(struct weston_keyboard *keyboard, -+ const struct timespec *time, uint32_t key, void *data) -+{ -+ struct desktop_shell *shell = data; -+ unsigned int new_index = shell->workspaces.current; -+ -+ if (shell->locked) -+ return; -+ if (new_index < shell->workspaces.num - 1) -+ new_index++; -+ -+ change_workspace(shell, new_index); -+} -+ -+static void -+workspace_f_binding(struct weston_keyboard *keyboard, -+ const struct timespec *time, uint32_t key, void *data) -+{ -+ struct desktop_shell *shell = data; -+ unsigned int new_index; -+ -+ if (shell->locked) -+ return; -+ new_index = key - KEY_F1; -+ if (new_index >= shell->workspaces.num) -+ new_index = shell->workspaces.num - 1; -+ -+ change_workspace(shell, new_index); -+} -+ -+static void -+workspace_move_surface_up_binding(struct weston_keyboard *keyboard, -+ const struct timespec *time, uint32_t key, -+ void *data) -+{ -+ struct desktop_shell *shell = data; -+ unsigned int new_index = shell->workspaces.current; -+ -+ if (shell->locked) -+ return; -+ -+ if (new_index != 0) -+ new_index--; -+ -+ take_surface_to_workspace_by_seat(shell, keyboard->seat, new_index); -+} -+ -+static void -+workspace_move_surface_down_binding(struct weston_keyboard *keyboard, -+ const struct timespec *time, uint32_t key, -+ void *data) -+{ -+ struct desktop_shell *shell = data; -+ unsigned int new_index = shell->workspaces.current; -+ -+ if (shell->locked) -+ return; -+ -+ if (new_index < shell->workspaces.num - 1) -+ new_index++; -+ -+ take_surface_to_workspace_by_seat(shell, keyboard->seat, new_index); -+} -+ - static void - shell_reposition_view_on_output_change(struct weston_view *view) - { -@@ -4287,12 +4795,16 @@ void - shell_for_each_layer(struct desktop_shell *shell, - shell_for_each_layer_func_t func, void *data) - { -+ struct workspace **ws; -+ - func(shell, &shell->fullscreen_layer, data); - func(shell, &shell->panel_layer, data); - func(shell, &shell->background_layer, data); - func(shell, &shell->lock_layer, data); - func(shell, &shell->input_panel_layer, data); -- func(shell, &shell->workspace.layer, data); -+ -+ wl_array_for_each(ws, &shell->workspaces.array) -+ func(shell, &(*ws)->layer, data); - } - - static void -@@ -4497,6 +5009,7 @@ shell_destroy(struct wl_listener *listener, void *data) - { - struct desktop_shell *shell = - container_of(listener, struct desktop_shell, destroy_listener); -+ struct workspace **ws; - struct shell_output *shell_output, *tmp; - struct shell_seat *shseat, *shseat_next; - -@@ -4529,7 +5042,9 @@ shell_destroy(struct wl_listener *listener, void *data) - - weston_desktop_destroy(shell->desktop); - -- workspace_destroy(&shell->workspace); -+ wl_array_for_each(ws, &shell->workspaces.array) -+ workspace_destroy(*ws); -+ wl_array_release(&shell->workspaces.array); - - desktop_shell_destroy_layer(&shell->panel_layer); - desktop_shell_destroy_layer(&shell->background_layer); -@@ -4546,6 +5061,7 @@ static void - shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) - { - uint32_t mod; -+ int i, num_workspace_bindings; - - if (shell->allow_zap) - weston_compositor_add_key_binding(ec, KEY_BACKSPACE, -@@ -4611,6 +5127,27 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) - ec); - weston_compositor_add_key_binding(ec, KEY_K, mod, - force_kill_binding, shell); -+ weston_compositor_add_key_binding(ec, KEY_UP, mod, -+ workspace_up_binding, shell); -+ weston_compositor_add_key_binding(ec, KEY_DOWN, mod, -+ workspace_down_binding, shell); -+ weston_compositor_add_key_binding(ec, KEY_UP, mod | MODIFIER_SHIFT, -+ workspace_move_surface_up_binding, -+ shell); -+ weston_compositor_add_key_binding(ec, KEY_DOWN, mod | MODIFIER_SHIFT, -+ workspace_move_surface_down_binding, -+ shell); -+ -+ /* Add bindings for mod+F[1-6] for workspace 1 to 6. */ -+ if (shell->workspaces.num > 1) { -+ num_workspace_bindings = shell->workspaces.num; -+ if (num_workspace_bindings > 6) -+ num_workspace_bindings = 6; -+ for (i = 0; i < num_workspace_bindings; i++) -+ weston_compositor_add_key_binding(ec, KEY_F1 + i, mod, -+ workspace_f_binding, -+ shell); -+ } - - weston_install_debug_key_binding(ec, mod); - } -@@ -4631,6 +5168,8 @@ wet_shell_init(struct weston_compositor *ec, - { - struct weston_seat *seat; - struct desktop_shell *shell; -+ struct workspace **pws; -+ unsigned int i; - struct wl_event_loop *loop; - - shell = zalloc(sizeof *shell); -@@ -4666,6 +5205,8 @@ wet_shell_init(struct weston_compositor *ec, - weston_layer_set_position(&shell->background_layer, - WESTON_LAYER_POSITION_BACKGROUND); - -+ wl_array_init(&shell->workspaces.array); -+ wl_list_init(&shell->workspaces.client_list); - wl_list_init(&shell->seat_list); - - if (input_panel_setup(shell) < 0) -@@ -4677,10 +5218,23 @@ wet_shell_init(struct weston_compositor *ec, - - shell_configuration(shell); - -- workspace_create(shell); -+ for (i = 0; i < shell->workspaces.num; i++) { -+ pws = wl_array_add(&shell->workspaces.array, sizeof *pws); -+ if (pws == NULL) -+ return -1; -+ -+ *pws = workspace_create(shell); -+ if (*pws == NULL) -+ return -1; -+ } -+ activate_workspace(shell, 0); - - weston_layer_init(&shell->minimized_layer, ec); - -+ wl_list_init(&shell->workspaces.anim_sticky_list); -+ wl_list_init(&shell->workspaces.animation.link); -+ shell->workspaces.animation.frame = animate_workspace_change_frame; -+ - shell->desktop = weston_desktop_create(ec, &shell_desktop_api, shell); - if (!shell->desktop) - return -1; -diff --git a/desktop-shell/shell.h b/desktop-shell/shell.h -index e9e123e9..f4cb40fd 100644 ---- a/desktop-shell/shell.h -+++ b/desktop-shell/shell.h -@@ -47,6 +47,7 @@ enum fade_type { - - struct focus_surface { - struct weston_curtain *curtain; -+ struct weston_transform workspace_transform; - }; - - struct workspace { -@@ -127,7 +128,21 @@ struct desktop_shell { - struct weston_surface *lock_surface; - struct wl_listener lock_surface_listener; - -- struct workspace workspace; -+ struct { -+ struct wl_array array; -+ unsigned int current; -+ unsigned int num; -+ -+ struct wl_list client_list; -+ -+ struct weston_animation animation; -+ struct wl_list anim_sticky_list; -+ int anim_dir; -+ struct timespec anim_timestamp; -+ double anim_current; -+ struct workspace *anim_from; -+ struct workspace *anim_to; -+ } workspaces; - - struct { - struct wl_resource *binding; -diff --git a/man/weston-bindings.man b/man/weston-bindings.man -index d528a807..bdeb9912 100644 ---- a/man/weston-bindings.man -+++ b/man/weston-bindings.man -@@ -58,6 +58,21 @@ Make the active window tiled bottom. - Switch active window - .P - .RE -+.B mod + Up, mod + Down -+.RS 4 -+Increment/decrement active workspace number, if there are multiple -+.P -+.RE -+.B mod + Shift + Up, mod + Shift + Down -+.RS 4 -+Move active window to the succeeding/preceding workspace, if possible -+.P -+.RE -+.B mod + F1/F2/F3/F4/F5/F6 -+.RS 4 -+Jump to the numbered workspace, if it exists -+.P -+.RE - .B Ctrl + Alt + Backspace - .RS 4 - If supported, terminate Weston. (Note this combination often is used to hard restart Xorg.) -diff --git a/man/weston.ini.man b/man/weston.ini.man -index 179e0882..d9b17d85 100644 ---- a/man/weston.ini.man -+++ b/man/weston.ini.man -@@ -426,6 +426,11 @@ for windows, controlling the backlight and zooming the desktop. See - .BR weston-bindings (7). - Possible values: none, ctrl, alt, super (default) - .TP 7 -+.BI "num-workspaces=" 6 -+defines the number of workspaces (unsigned integer). The user can switch -+workspaces by using the -+binding+F1, F2 keys. If this key is not set, fall back to one workspace. -+.TP 7 - .BI "cursor-theme=" theme - sets the cursor theme (string). - .TP 7 diff --git a/packages/audio-ctrl/default.nix b/packages/audio-ctrl/default.nix new file mode 100644 index 000000000..07644a091 --- /dev/null +++ b/packages/audio-ctrl/default.nix @@ -0,0 +1,29 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# This is a temporary solution for volume control. +# +{ pamixer, writeShellApplication, ... }: +writeShellApplication { + name = "audio-ctrl"; + runtimeInputs = [ pamixer ]; + text = '' + export PULSE_SERVER=audio-vm:4713 + + case "$1" in + inc) + pamixer -i 5 + ;; + dec) + pamixer -d 5 + ;; + mut) + if [ "$(pamixer --get-mute)" = "false" ]; then + pamixer -m + else + pamixer -u + fi + ;; + esac + ''; +} diff --git a/packages/dendrite-pinecone/default.nix b/packages/dendrite-pinecone/default.nix new file mode 100644 index 000000000..bb073e164 --- /dev/null +++ b/packages/dendrite-pinecone/default.nix @@ -0,0 +1,27 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ buildGoModule, fetchFromGitHub }: +buildGoModule { + pname = "dendrite-pinecone"; + version = "0.9.1"; + + # TODO: Move all these to the options module. + TcpPort = "49000"; + McastUdpPort = "60606"; + McastUdpIp = "239.0.0.114"; + TcpPortInt = 49000; + McastUdpPortInt = 60606; + + src = fetchFromGitHub { + owner = "tiiuae"; + repo = "dendrite"; + # branch is feature/ghaf-integration + rev = "d8e62f3d1bf6607d243c53673fc02064fed863e8"; + sha256 = "sha256-GtaFDfXssym3eNrTSOB8JW2awIvZsTGdUPdLL+ae7Pw="; + }; + subPackages = [ "cmd/dendrite-demo-pinecone" ]; + # patches = [./turnserver-crendentials-flags.patch]; + + vendorHash = "sha256-599pZlX7SdUYOmGnYGIngyPKagIxri6KKJh+e5UDBps="; +} diff --git a/packages/element-gps/default.nix b/packages/element-gps/default.nix new file mode 100644 index 000000000..457e55569 --- /dev/null +++ b/packages/element-gps/default.nix @@ -0,0 +1,12 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ python3Packages }: +with python3Packages; +buildPythonApplication { + pname = "gpswebsock"; + version = "1.0"; + + propagatedBuildInputs = [ websockets ]; + + src = ./.; +} diff --git a/packages/element-gps/main.py b/packages/element-gps/main.py new file mode 100755 index 000000000..74cccbe16 --- /dev/null +++ b/packages/element-gps/main.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python + +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 + +import asyncio +import functools +import json +import signal +import threading + +import websockets + + +class GpsProcessState: + def __init__(self): + self._gps_data = "" + self.data_lock = asyncio.Lock() + self.condition = asyncio.Condition() + self.abort_websockets = False + self.terminate = asyncio.Event() + self.stop_event = threading.Event() + self.stop_wait_asyncio = asyncio.get_event_loop().run_in_executor( + None, self.stop_event.wait + ) + + def get_data(self): + return self._gps_data + + def set_data(self, value): + self._gps_data = value + + def del_data(self): + del self._gps_data + + message = property(get_data, set_data, del_data) + + +async def read_continuous_gps(data): + process = await asyncio.create_subprocess_exec( + "./run/current-system/sw/bin/gpspipe", + "-w", + stdin=asyncio.subprocess.PIPE, + stdout=asyncio.subprocess.PIPE, + ) + print("GPS reader process PID:", {process.pid}, "starting...") + + while not data.terminate.is_set(): + if process.returncode is not None: + print("gpspipe process has exited") + break + + line = await process.stdout.readline() + line = line.decode() + + if len(line) != 0: + reply_json = json.loads(line) + if reply_json["class"] == "TPV": + async with data.data_lock: + data.message = line + async with data.condition: + data.condition.notify_all() + + print("Closing service...") + # Notify all websockets to quit + async with data.condition: + data.abort_websockets = True + data.condition.notify_all() + await asyncio.sleep(2) + data.stop_event.set() + + +async def handler(websocket, path, gps_state): + print("New connection received") + while not gps_state.abort_websockets: + async with gps_state.condition: + await gps_state.condition.wait() + if gps_state.abort_websockets: + break + async with gps_state.data_lock: + output = gps_state.message + try: + await websocket.send(output) + except Exception: + print("Client disconnected.") + break + print("Closing websocket...") + + +async def wait_connection(gps_state): + print("Websocket listener on localhost:8000.") + async with websockets.serve( + functools.partial(handler, gps_state=gps_state), "localhost", 8000 + ): + await gps_state.stop_wait_asyncio + print("Closing websocket listener.") + + +def signal_handler(signum, frame, state_object): + # ignore additional signals + signal.signal(signum, signal.SIG_IGN) + state_object.terminate.set() + + +async def main(): + gps_state = GpsProcessState() + # The stop condition is set when receiving SIGTERM or SIGINT. + signal.signal( + signal.SIGINT, functools.partial(signal_handler, state_object=gps_state) + ) + signal.signal( + signal.SIGTERM, functools.partial(signal_handler, state_object=gps_state) + ) + await asyncio.gather(read_continuous_gps(gps_state), wait_connection(gps_state)) + + +if __name__ == "__main__": + loop = asyncio.get_event_loop() + stop = loop.create_future() + loop.add_signal_handler(signal.SIGTERM, stop.set_result, None) + try: + loop.run_until_complete(main()) + finally: + loop.close() diff --git a/packages/element-gps/setup.py b/packages/element-gps/setup.py new file mode 100644 index 000000000..a0e2748ba --- /dev/null +++ b/packages/element-gps/setup.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 + +from setuptools import find_packages, setup + +setup( + name="gpswebsock", + version="1.0", + # Modules to import from other scripts: + packages=find_packages(), + # Executables + scripts=["main.py"], +) diff --git a/packages/element-web/default.nix b/packages/element-web/default.nix new file mode 100644 index 000000000..76c7f67e8 --- /dev/null +++ b/packages/element-web/default.nix @@ -0,0 +1,97 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + stdenv, + fetchFromGitHub, + fetchYarnDeps, + jq, + yarn, + fixup_yarn_lock, + nodejs, + jitsi-meet, +}: +let + pinData = import ./pin.nix; + inherit (pinData.hashes) webSrcHash webYarnHash; + noPhoningHome = { + disable_guests = true; # disable automatic guest account registration at matrix.org + }; +in +stdenv.mkDerivation ( + finalAttrs: + builtins.removeAttrs pinData [ "hashes" ] + // { + pname = "element-web"; + + src = fetchFromGitHub { + owner = "vector-im"; + repo = finalAttrs.pname; + rev = "v${finalAttrs.version}"; + hash = webSrcHash; + }; + + offlineCache = fetchYarnDeps { + yarnLock = finalAttrs.src + "/yarn.lock"; + sha256 = webYarnHash; + }; + + nativeBuildInputs = [ + yarn + fixup_yarn_lock + jq + nodejs + ]; + + configurePhase = '' + runHook preConfigure + + export HOME=$PWD/tmp + # with the update of openssl3, some key ciphers are not supported anymore + # this flag will allow those codecs again as a workaround + # see https://medium.com/the-node-js-collection/node-js-17-is-here-8dba1e14e382#5f07 + # and https://github.com/vector-im/element-web/issues/21043 + export NODE_OPTIONS=--openssl-legacy-provider + mkdir -p $HOME + + fixup_yarn_lock yarn.lock + yarn config --offline set yarn-offline-mirror $offlineCache + yarn install --offline --frozen-lockfile --ignore-platform --ignore-scripts --no-progress --non-interactive + patch -p1 < ${./matrix-react-sdk.patch} + patchShebangs node_modules + + runHook postConfigure + ''; + + buildPhase = '' + runHook preBuild + + export VERSION=${finalAttrs.version} + yarn build:res --offline + yarn build:module_system --offline + yarn build:bundle --offline + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + cp -R webapp $out + cp ${jitsi-meet}/libs/external_api.min.js $out/jitsi_external_api.min.js + echo "${finalAttrs.version}" > "$out/version" + jq -s '.[0] * $conf' "config.sample.json" --argjson "conf" '${builtins.toJSON noPhoningHome}' > "$out/config.json" + + runHook postInstall + ''; + + meta = { + description = "A glossy Matrix collaboration client for the web"; + homepage = "https://element.io/"; + changelog = "https://github.com/vector-im/element-web/blob/v${finalAttrs.version}/CHANGELOG.md"; + maintainers = lib.teams.matrix.members; + license = lib.licenses.asl20; + platforms = lib.platforms.all; + }; + } +) diff --git a/packages/element-web/matrix-react-sdk.patch b/packages/element-web/matrix-react-sdk.patch new file mode 100644 index 000000000..00c8bdab2 --- /dev/null +++ b/packages/element-web/matrix-react-sdk.patch @@ -0,0 +1,206 @@ +diff --unified --recursive --text --color element-web.orig/node_modules/matrix-react-sdk/src/components/views/location/LocationPicker.tsx element-web.new/node_modules/matrix-react-sdk/src/components/views/location/LocationPicker.tsx +--- element-web.orig/node_modules/matrix-react-sdk/src/components/views/location/LocationPicker.tsx 2023-10-17 09:32:00.594000000 +0300 ++++ element-web.new/node_modules/matrix-react-sdk/src/components/views/location/LocationPicker.tsx 2023-10-18 11:18:59.659372442 +0300 +@@ -45,6 +45,188 @@ + error?: LocationShareError; + } + ++interface Coordinates { ++ latitude: number; ++ longitude: number; ++ altitude?: number | null; ++ accuracy?: number; ++ altitudeAccuracy?: number | null; ++ heading?: number | null; ++ speed?: number | null; ++} ++ ++interface Position { ++ coords: Coordinates; ++ timestamp: number; ++ isHighAccuracy?: boolean; ++} ++ ++interface PositionError { ++ code: number; ++ message: string; ++} ++ ++class ExternalGeolocation { ++ private static sockets: Map = new Map(); ++ private static cachedPosition: Position | null = null; ++ ++ static getCurrentPosition( ++ successCallback: (position: Position) => void, ++ errorCallback?: (error: PositionError) => void, ++ options?: PositionOptions ++ ){ ++ if (ExternalGeolocation.cachedPosition && options?.maximumAge && options.maximumAge > 0 && Date.now() - options.maximumAge > ExternalGeolocation.cachedPosition.timestamp ) { ++ successCallback(ExternalGeolocation.cachedPosition); ++ return; ++ } ++ ++ const socket = new WebSocket("ws://localhost:8000"); ++ ++ let timeout: ReturnType; ++ if (options?.timeout) { ++ timeout = setInterval(function () { ++ socket.close(); ++ if (errorCallback) { ++ errorCallback({ ++ code: 3, ++ message: `Timeout elapsed: ${options.timeout}ms` ++ }); ++ } ++ }, options.timeout); ++ } ++ ++ socket.onmessage = (event) => { ++ const p = JSON.parse(event.data); ++ ++ if (p.mode > 1) { ++ if (timeout) { ++ clearInterval(timeout); ++ } ++ socket.close(); ++ ++ const timestamp = new Date(p.time).getTime(); ++ ++ const position: Position = { ++ coords: { ++ latitude: p.lat, ++ longitude: p.lon, ++ accuracy: p.eph, ++ altitude: p.altMSL, ++ altitudeAccuracy: p.epv, ++ heading: p.track, ++ speed: p.speed, ++ }, ++ timestamp, ++ isHighAccuracy: true, ++ }; ++ ++ ExternalGeolocation.cachedPosition = position; ++ successCallback(position); ++ } ++ }; ++ ++ socket.onerror = (event) => { ++ if (timeout) { ++ clearInterval(timeout); ++ } ++ if (errorCallback) { ++ errorCallback({ ++ code: 2, ++ message: `WebSocket error: ${event.type}` ++ }); ++ } ++ }; ++ } ++ ++ static watchPosition( ++ successCallback: (position: Position) => void, ++ errorCallback?: (error: PositionError) => void, ++ options?: PositionOptions ++ ){ ++ const watchId = ExternalGeolocation.sockets.size; ++ ++ if (ExternalGeolocation.cachedPosition && options?.maximumAge && options.maximumAge > 0 && Date.now() - options.maximumAge > ExternalGeolocation.cachedPosition.timestamp ) { ++ successCallback(ExternalGeolocation.cachedPosition); ++ } ++ ++ const socket = new WebSocket("ws://localhost:8000"); ++ ++ let timeout: ReturnType; ++ if (options?.timeout) { ++ timeout = setInterval(function () { ++ socket.close(); ++ if (errorCallback) { ++ errorCallback({ ++ code: 3, ++ message: `Timeout elapsed: ${options.timeout}ms` ++ }); ++ } ++ }, options.timeout); ++ } ++ ++ socket.onmessage = (event) => { ++ const p = JSON.parse(event.data); ++ const timestamp = new Date(p.time).getTime(); ++ ++ if (p.mode > 1) { ++ if (timeout) { ++ clearInterval(timeout); ++ } ++ const position: Position = { ++ coords: { ++ latitude: p.lat, ++ longitude: p.lon, ++ accuracy: p.eph, ++ altitude: p.altMSL, ++ altitudeAccuracy: p.epv, ++ heading: p.track, ++ speed: p.speed, ++ }, ++ timestamp, ++ isHighAccuracy: true, ++ }; ++ ++ ExternalGeolocation.cachedPosition = position; ++ successCallback(position); ++ } ++ }; ++ ++ socket.onerror = (event) => { ++ if (timeout) { ++ clearInterval(timeout); ++ } ++ ++ if (errorCallback) { ++ errorCallback({ ++ code: 2, ++ message: `WebSocket error: ${event.type}`, ++ }); ++ } ++ }; ++ ++ ExternalGeolocation.sockets.set(watchId, socket); ++ ++ return watchId; ++ } ++ ++ static clearWatch(watchId: number) { ++ const socket = ExternalGeolocation.sockets.get(watchId); ++ if (socket) { ++ socket.close(); ++ ExternalGeolocation.sockets.delete(watchId); ++ } ++ } ++} ++ ++Object.defineProperty(navigator, 'geolocation', { ++ value: { ++ getCurrentPosition: ExternalGeolocation.getCurrentPosition, ++ watchPosition: ExternalGeolocation.watchPosition, ++ clearWatch: ExternalGeolocation.clearWatch, ++ }, ++ writable: false, ++}); ++ + const isSharingOwnLocation = (shareType: LocationShareType): boolean => + shareType === LocationShareType.Own || shareType === LocationShareType.Live; + +diff --unified --recursive --text --color element-web.orig/node_modules/matrix-react-sdk/src/components/views/rooms/MessageComposer.tsx element-web.new/node_modules/matrix-react-sdk/src/components/views/rooms/MessageComposer.tsx +--- element-web.orig/node_modules/matrix-react-sdk/src/components/views/rooms/MessageComposer.tsx 2023-10-17 09:32:00.594000000 +0300 ++++ element-web.new/node_modules/matrix-react-sdk/src/components/views/rooms/MessageComposer.tsx 2023-10-17 09:49:26.617200503 +0300 +@@ -624,9 +624,7 @@ + relation={this.props.relation} + onRecordStartEndClick={this.onRecordStartEndClick} + setStickerPickerOpen={this.setStickerPickerOpen} +- showLocationButton={ +- !window.electron && SettingsStore.getValue(UIFeature.LocationSharing) +- } ++ showLocationButton={SettingsStore.getValue(UIFeature.LocationSharing)} + showPollsButton={this.state.showPollsButton} + showStickersButton={this.showStickersButton} + isRichTextEnabled={this.state.isRichTextEnabled} diff --git a/packages/element-web/pin.nix b/packages/element-web/pin.nix new file mode 100644 index 000000000..6bcb14e78 --- /dev/null +++ b/packages/element-web/pin.nix @@ -0,0 +1,11 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + "version" = "1.11.47"; + "hashes" = { + "desktopSrcHash" = "sha256-Ea3LJt+3HAOX2PTREMojtuPVTeA6u7VJtysd8bbTbwU="; + "desktopYarnHash" = "1nssv92yk1a53v7mvijkrb3gzif5xrz2j6lxvg7p340z42rm7f9v"; + "webSrcHash" = "sha256-DPBMZMjDnwjdNsePcUBBU0KRGOpOmNHgQZn9/ad/Qss="; + "webYarnHash" = "0rzipmaq0jarzdawl7lmxnapwzl52kklxadm859hgx9b1hd5vwj7"; + }; +} diff --git a/packages/flake-module.nix b/packages/flake-module.nix index 556c5be10..a592437e8 100644 --- a/packages/flake-module.nix +++ b/packages/flake-module.nix @@ -1,41 +1,49 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{self, ...}: { - flake.packages.riscv64-linux.hart-software-services = - self.nixosConfigurations.microchip-icicle-kit-debug.pkgs.callPackage ./hart-software-services {}; - perSystem = { - pkgs, - lib, - system, - ... - }: let - inherit (pkgs) callPackage; - in { - packages = self.lib.platformPkgs system { - gala-app = callPackage ./gala {}; - kernel-hardening-checker = callPackage ./kernel-hardening-checker {}; - windows-launcher = callPackage ./windows-launcher {enableSpice = false;}; - windows-launcher-spice = callPackage ./windows-launcher {enableSpice = true;}; - doc = callPackage ../docs { - revision = lib.strings.fileContents ../.version; - # options = ; - # TODO Add the options in from the self.nixosModules - # The below is not needed anymore to setoptions - # - # options = let - # cfg = nixpkgs.lib.nixosSystem { - # inherit system; - # modules = - # lib.ghaf.modules - # ++ [ - # jetpack-nixos.nixosModules.default - # microvm.nixosModules.host - # lanzaboote.nixosModules.lanzaboote - # ]; - # }; - # in - # cfg.options; +{ self, ... }: +{ + flake.packages.x86_64-linux.hart-software-services = + self.nixosConfigurations.microchip-icicle-kit-debug-from-x86_64.pkgs.callPackage + ./hart-software-services + { }; + perSystem = + { + pkgs, + lib, + system, + ... + }: + let + inherit (pkgs) callPackage; + in + { + packages = self.lib.platformPkgs system { + gala-app = callPackage ./gala { }; + kernel-hardening-checker = callPackage ./kernel-hardening-checker { }; + make-checks = callPackage ./make-checks { }; + windows-launcher = callPackage ./windows-launcher { enableSpice = false; }; + windows-launcher-spice = callPackage ./windows-launcher { enableSpice = true; }; + hardware-scan = callPackage ./hardware-scan { }; + doc = callPackage ../docs { + revision = lib.strings.fileContents ../.version; + # options = ; + # TODO Add the options in from the self.nixosModules + # The below is not needed anymore to setoptions + # + # options = let + # cfg = nixpkgs.lib.nixosSystem { + # inherit system; + # modules = + # lib.ghaf.modules + # ++ [ + # jetpack-nixos.nixosModules.default + # microvm.nixosModules.host + # lanzaboote.nixosModules.lanzaboote + # ]; + # }; + # in + # cfg.options; + }; }; }; - }; } diff --git a/packages/flash/default.nix b/packages/flash/default.nix new file mode 100644 index 000000000..5a7c4ecbe --- /dev/null +++ b/packages/flash/default.nix @@ -0,0 +1,17 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + coreutils, + util-linux, + writeShellApplication, + zstd, +}: +writeShellApplication { + name = "flash-script"; + runtimeInputs = [ + coreutils + util-linux + zstd + ]; + text = builtins.readFile ./flash.sh; +} diff --git a/packages/flash/flash.sh b/packages/flash/flash.sh new file mode 100755 index 000000000..b952000eb --- /dev/null +++ b/packages/flash/flash.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 + +# Function to print usage and exit +print_usage() { + echo "Usage: $0 -d -i " + exit 1 +} + +if [ "$EUID" -ne 0 ]; then + echo "Please run as root" + exit +fi + +# Check the number of parameters +if [ "$#" -ne 4 ]; then + print_usage +fi + +# Parse the parameters +while getopts "d:i:" opt; do + case $opt in + d) DEVICE="$OPTARG" ;; + i) FILENAME="$OPTARG" ;; + *) print_usage ;; + esac +done + +# Check if disk and imagefile exist +if [ ! -e "$DEVICE" ]; then + echo "No such block device: ${DEVICE}" + exit 1 +fi + +if [ ! -e "$FILENAME" ]; then + echo "No such file: ${FILENAME}" + exit 1 +fi + +# Function to wipe any ZFS leftovers exising on the disk +wipe_filesystem () { + echo "Wiping filesystem..." + # Set sector size to 512 bytes + SECTOR=512 + # 10 MiB in 512-byte sectors + MIB_TO_SECTORS=20480 + # Disk size in 512-byte sectors + SECTORS=$(blockdev --getsz "$DEVICE") + # Unmount possible mounted filesystems + sync; umount -q "$DEVICE"* || true; + # Wipe first 10MiB of disk + dd if=/dev/zero of="$DEVICE" bs="$SECTOR" count="$MIB_TO_SECTORS" conv=fsync status=none + # Wipe last 10MiB of disk + dd if=/dev/zero of="$DEVICE" bs="$SECTOR" count="$MIB_TO_SECTORS" seek="$((SECTORS - MIB_TO_SECTORS))" conv=fsync status=none + echo "Flashing..." +} + +echo "Found ${FILENAME}..." + +# Check the extension of the image file and run appropriate command +if [[ "$FILENAME" == *.zst ]]; then + wipe_filesystem + zstdcat "$FILENAME" | dd of="$DEVICE" bs=32M status=progress conv=fsync oflag=direct iflag=fullblock +elif [[ "$FILENAME" == *.iso || "$FILENAME" == *.img ]]; then + wipe_filesystem + dd if="$FILENAME" of="$DEVICE" bs=32M status=progress conv=fsync oflag=direct iflag=fullblock +else + echo "Unsupported file format" + exit 1 +fi diff --git a/packages/gala/default.nix b/packages/gala/default.nix index 13bf710e3..10c2bdc25 100644 --- a/packages/gala/default.nix +++ b/packages/gala/default.nix @@ -40,7 +40,8 @@ mesa, unzip, wayland, -}: let +}: +let dynamic-linker = stdenv.cc.bintools.dynamicLinker; libPath = lib.makeLibraryPath [ @@ -82,57 +83,56 @@ wayland ]; in - stdenv.mkDerivation rec { - name = "gala"; +stdenv.mkDerivation rec { + name = "gala"; - nativeBuildInputs = [unzip]; + nativeBuildInputs = [ unzip ]; - buildInputs = [unzip]; + buildInputs = [ unzip ]; - # See meta.platforms section for supported platforms - src = - if stdenv.isAarch64 - then - fetchurl { - url = "https://vedenemo.dev/files/gala/eb56901d-410c-4c09-bbac-9e954a3f16b0-gala-electron-test-0.1.26-arm64.zip"; - sha256 = "16d8g6h22zsnw4kq8nkama5yxp5swn7fj8m197kgm58w3dai3mn7"; - } - else - fetchurl { - url = "https://vedenemo.dev/files/gala/eb56901d-410c-4c09-bbac-9e954a3f16b0-gala-electron-test-0.1.26.zip"; - sha256 = "0chn1rbdvs71mxfdwpld4v2zdg2crrqln9ckscivas48rmg6sj6f"; - }; + # See meta.platforms section for supported platforms + src = + if stdenv.isAarch64 then + fetchurl { + url = "https://vedenemo.dev/files/gala/eb56901d-410c-4c09-bbac-9e954a3f16b0-gala-electron-test-0.1.26-arm64.zip"; + sha256 = "16d8g6h22zsnw4kq8nkama5yxp5swn7fj8m197kgm58w3dai3mn7"; + } + else + fetchurl { + url = "https://vedenemo.dev/files/gala/eb56901d-410c-4c09-bbac-9e954a3f16b0-gala-electron-test-0.1.26.zip"; + sha256 = "0chn1rbdvs71mxfdwpld4v2zdg2crrqln9ckscivas48rmg6sj6f"; + }; - phases = "unpackPhase fixupPhase"; - targetPath = "$out/gala"; - intLibPath = "$out/gala/swiftshader"; + phases = "unpackPhase fixupPhase"; + targetPath = "$out/gala"; + intLibPath = "$out/gala/swiftshader"; - unpackPhase = '' - mkdir -p ${targetPath} - unzip $src -d ${targetPath} - ''; + unpackPhase = '' + mkdir -p ${targetPath} + unzip $src -d ${targetPath} + ''; - rpath = lib.concatStringsSep ":" [ - libPath - targetPath - intLibPath - ]; + rpath = lib.concatStringsSep ":" [ + libPath + targetPath + intLibPath + ]; - fixupPhase = '' - patchelf \ - --set-interpreter "${dynamic-linker}" \ - --set-rpath "${rpath}" \ - ${targetPath}/dev.scpp.saca.gala + fixupPhase = '' + patchelf \ + --set-interpreter "${dynamic-linker}" \ + --set-rpath "${rpath}" \ + ${targetPath}/dev.scpp.saca.gala - mkdir -p $out/bin - ln -s $out/gala/dev.scpp.saca.gala $out/bin/gala - ''; + mkdir -p $out/bin + ln -s $out/gala/dev.scpp.saca.gala $out/bin/gala + ''; - meta = with lib; { - description = "Google Android look-alike"; - platforms = [ - "aarch64-linux" - "x86_64-linux" - ]; - }; - } + meta = with lib; { + description = "Google Android look-alike"; + platforms = [ + "aarch64-linux" + "x86_64-linux" + ]; + }; +} diff --git a/packages/ghaf-open/default.nix b/packages/ghaf-open/default.nix new file mode 100644 index 000000000..362d16e41 --- /dev/null +++ b/packages/ghaf-open/default.nix @@ -0,0 +1,38 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# A debug script that allows executing applications from the command line. +{ writeShellApplication, gawk, ... }: +writeShellApplication { + name = "ghaf-open"; + runtimeInputs = [ gawk ]; + text = '' + APPS=/run/current-system/sw/share/applications + + function list_apps() { + for e in "$APPS"/*.desktop; do + [[ -e "$e" ]] || continue # in case of no entries + + basename "$e" .desktop + done + } + + if [ $# -eq 0 ]; then + echo -e "Usage: ghaf-open <-l|application> [args...]\n" + echo -e "\t-l\tList available applications" + exit 1 + fi + + if [ "$1" = "-l" ]; then + list_apps + exit 0 + fi + + if [ ! -e "$APPS/$1.desktop" ]; then + echo "No launcher entry for $1" + exit 1 + fi + + eval "$(awk '/^Exec=/{sub(/^Exec=/, ""); print}' "$APPS/$1.desktop") ''${*:2}" + ''; +} diff --git a/packages/hardware-scan/default.nix b/packages/hardware-scan/default.nix new file mode 100644 index 000000000..9864c0f0b --- /dev/null +++ b/packages/hardware-scan/default.nix @@ -0,0 +1,28 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# This is a temporary solution for hardware detection. +# +{ + writeShellApplication, + util-linux, + pciutils, + usbutils, + dmidecode, + alejandra, +}: +writeShellApplication { + name = "hardware-scan"; + runtimeInputs = [ + util-linux + pciutils + usbutils + dmidecode + alejandra + ]; + text = builtins.readFile ./hardware-scan.sh; + meta = { + description = "Helper script for hardware discovery and configuration file generation"; + platforms = [ "x86_64-linux" ]; + }; +} diff --git a/packages/hardware-scan/hardware-scan.sh b/packages/hardware-scan/hardware-scan.sh new file mode 100755 index 000000000..785ee9702 --- /dev/null +++ b/packages/hardware-scan/hardware-scan.sh @@ -0,0 +1,631 @@ +#! /usr/bin/env bash +# shellcheck shell=bash +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 + +# Notes: +# 1. This script needs to run on a nix-enabled system, with either internet access or packages installed +# 2. The script uses lspci and dmidecode to detect system information and hardware devices, using simple key words +# 3. The host this script runs on needs to be able to detect all hardware, e.g., no passthrough should be enabled +# 4. Do not attach external devices when running this script +# 5. The script generates commented-out sections for some input devices, which need to be manually selected as many of them need to stay in the host +# 6. Do NOT rely fully on the output of this script; especially for kernel modules and parameters! +# 7. Use the hardware dump generated with the '-e' option to inspect the hardware and manually adjust the configuration file +# 8. If you copied this script, include the following: +# #! /usr/bin/env nix-shell +# #! nix-shell -i bash -p pciutils dmidecode usbutils alejandra + +usage() { +cat < pci_info.txt). +EOF +} + +set +xo pipefail + +# Global Variables +CONFIG_FILE="" # name is auto-generated from system information (-y, -a) +system_name="" +system_sku="" +declare -A pci_devices=() +declare -A kernel_modules=() +keyboard_attr_names=() +mouse_attr_names=() +touchpad_attr_names=() +misc_devlinks=() +misc_attr_names=() +usb_devices=() +disk="" +host_blacklist="" + +# Default device groups +NET_ID_1="wlp0s5f" +NET_ID_2="eth" +GPU_ID="gpu" +SND_ID="snd" + +# Default entries +pci_devices[$NET_ID_1]="" +pci_devices[$NET_ID_2]="" +pci_devices[$GPU_ID]="" +pci_devices[$SND_ID]="" +kernel_modules[$NET_ID_1]="" +kernel_modules[$NET_ID_2]="" +kernel_modules[$GPU_ID]="" +kernel_modules[$SND_ID]="" + +# Detecting system information via dmidecode +detect_system_info() { + system_manufacturer=$(sudo dmidecode -s system-manufacturer) + system_version=$(sudo dmidecode -s system-version) + system_product_name=$(sudo dmidecode -s system-product-name) + system_sku_number=$(sudo dmidecode -s system-sku-number) + system_name="$system_manufacturer $system_version" + system_sku="$system_sku_number $system_product_name" + CONFIG_FILE="$system_name.nix" + CONFIG_FILE=${CONFIG_FILE// /-} + CONFIG_FILE=${CONFIG_FILE,,} + if $verbose; then + echo "System: $system_name" + echo "SKU: $system_sku" + fi +} + +### PCI DEVICE DETECTION ### + +add_pci_device () { + + # Parse input params + local pci_device="$1" + local device_group_name="$2" + local group_num="$3" + local devices=() + local drivers="" + local modules="" + + # Find all devices in the IOMMU group + pci_address=$(echo "$pci_device" | awk '{print $1}') + iommu_group=$(lspci -vns "$pci_address" | grep "IOMMU group" | awk -F "IOMMU group " '{print $2}') + iommu_path="/sys/kernel/iommu_groups/$iommu_group" + readarray -t iommu_devices <<< "$(find "$iommu_path" -type l | awk -F "$iommu_path/devices/" '{print $2}')" + + # Add entries for each device in the IOMMU group + for i in "${!iommu_devices[@]}"; do + + # Fetch info + address=${iommu_devices[$i]} + name=$(lspci -s "$address" | cut -d " " -f 2-) + vendor_id=$(lspci -mmns "$address" | awk '{print $3}') + product_id=$(lspci -mmns "$address" | awk '{print $4}') + drv=$(lspci -nnks "$address" | grep "Kernel driver in use:" | awk -F ": " '{print $2}' | tr -d '[:space:]') + mods=$(lspci -nnks "$address" | grep "Kernel modules:" | awk -F ": " '{print $2}' | tr -d '[:space:]') + + # Check if device is already passed through + if echo "${pci_devices[*]}" | grep -q "$address"; then + echo -e "\n# Error: Cannot add $pci_address to $device_group_name$group_num: already passed through." + echo "# Device $pci_address and $address are in the same IOMMU group $iommu_group:" + for j in "${!iommu_devices[@]}"; do echo -n "# "; lspci -s "${iommu_devices[$j]}"; done + echo -e "# Skipping passthrough of device $pci_address\n" + return 0 + fi + + # Create device name and entry + local n=""; if [ "${#iommu_devices[@]}" -gt 1 ]; then n="-$i"; fi + device_name="$device_group_name$group_num$n" + devices+=("$(cat << EOF +{ + # $name + name = "$device_name"; + path = "$address"; + vendorId = $vendor_id; + productId = $product_id; + # Detected kernel driver: $drv + # Detected kernel modules: $mods +} +EOF +)") + drivers="$drivers,$drv" + modules="$modules,$mods" + done + + if $verbose; then + echo -e "Device entry: \n${devices[*]}" + fi + + modules=${modules#','} + modules=$(echo "$modules" | tr ',' '\n' | sort -u | tr '\n' ',') + modules=${modules%','} + + local modules_to_load=() + IFS=',' + for elem in $modules; do + if [ -n "$elem" ]; then modules_to_load+=("\"$elem\""); fi + done + IFS= + + # Add detected devices and kernel modules to global arrays + pci_devices["$device_group_name"]="${devices[*]}" + kernel_modules["$device_group_name"]="${modules_to_load[*]}" + + # Add kernel modules to host blacklist + if [ "${modules}" != "" ]; then + host_blacklist="$host_blacklist,$modules" + fi + host_blacklist=$(echo "$host_blacklist" | tr ',' '\n' | sort -u | tr '\n' ',') + host_blacklist=${host_blacklist%','} + host_blacklist=${host_blacklist#','} +} + +detect_pci_devices() { + + # Check if IOMMU is enabled + if [ -z "$(ls -A /sys/kernel/iommu_groups)" ]; then + echo "# It seems that the IOMMU groups are not setup (ls /sys/kernel/iommu_groups). Please enable virtualization in the BIOS, and/or pass the respective kernel parameters." + fi + + # Parse params + if [ "$#" -eq 0 ]; then + read -r -p "Enter search pattern for PCI devices: " search_pattern + read -r -p "Enter group id PCI devices: " group_id + group_id=${group_id:-"pci"} + else + local search_pattern="$1" + local group_id="$2" + fi + + # Search for PCI devices + readarray -t pci_devs <<< "$(lspci -nn | grep -i "$search_pattern")" + + # Select PCI devices + if [ ${#pci_devs[@]} -ge 1 ] && [ -n "${pci_devs[0]}" ]; then + local n=0 + for i in "${!pci_devs[@]}"; do + read -r -p "Select '${pci_devs[$i]}' for passthrough? [Y/n] " answer + answer=${answer:-Y} + case $answer in + [Yy]* ) add_pci_device "${pci_devs[$i]}" "$group_id" "$n"; n=$((n+1)); continue;; + [Nn]* ) continue;; + * ) echo "Please answer yY or nN.";; + esac + done + else + echo "No device found searching for '$search_pattern'." + return + fi +} + +### INPUT DEVICE DETECTION ### + +# Search for input devices using /dev/input/event* +detect_input_devices() { + + local input_events=() + local keyboard_devices=() + local mouse_devices=() + local touchpad_devices=() + + while IFS= read -r line; do + input_events+=("$line") + done <<< "$(ls /dev/input/event*)" + + # Use udevadm to iterate through input_events and determine devices + for event in "${input_events[@]}"; do + device_info=$(udevadm info --query=all --name="$event") + if [[ $device_info =~ ID_INPUT_KEYBOARD=1 ]]; then + keyboard_devices+=("$event") + elif [[ $device_info =~ ID_INPUT_KEY=1 ]] || [[ $device_info =~ ID_INPUT_SWITCH=1 ]]; then + misc_devices+=("$event") + fi + if [[ $device_info =~ ID_INPUT_MOUSE=1 ]] && ! [[ $device_info =~ ID_INPUT_TOUCHPAD=1 ]]; then + mouse_devices+=("$event") + elif [[ $device_info =~ ID_INPUT_TOUCHPAD=1 ]]; then + touchpad_devices+=("$event") + fi + if [[ $device_info =~ ID_INPUT_TOUCHSCREEN=1 ]] || [[ $device_info =~ ID_INPUT_TABLET=1 ]]; then + touchpad_devices+=("$event") + fi + done + # Check if any keyboard device found + if [ ${#keyboard_devices[@]} -eq 0 ]; then + echo "# No keyboard device found." + fi + # Check if any mouse device found + if [ ${#mouse_devices[@]} -eq 0 ]; then + echo "# No mouse device found." + fi + + # Use udevadm to query keyboard info + tmp_names=() + for event in "${keyboard_devices[@]}"; do + keyboard_attr_name=$(udevadm info -a "$event" | grep "ATTRS{name}" | head -1 | awk -F "==" '{print $2}' | tr -d '\n') + tmp_names+=("$keyboard_attr_name") + done + # Remove duplicates + for elem in "${tmp_names[@]}"; do + if [[ ! " ${keyboard_attr_names[*]} " =~ $elem ]]; then + keyboard_attr_names+=("$elem") + fi + done + + # Use udevadm to query mouse info + tmp_names=() + for event in "${mouse_devices[@]}"; do + mouse_attr_name=$(udevadm info -a "$event" | grep "ATTRS{name}" | head -1 | awk -F "==" '{print $2}' | tr -d '\n') + tmp_names+=("$mouse_attr_name") + done + + # Remove duplicates + for elem in "${tmp_names[@]}"; do + if [[ ! " ${mouse_attr_names[*]} " =~ $elem ]]; then + mouse_attr_names+=("$elem") + fi + done + + # Use udevadm to query touchpad info + tmp_names=() + for event in "${touchpad_devices[@]}"; do + touchpad_attr_name=$(udevadm info -a "$event" | grep "ATTRS{name}" | head -1 | awk -F "==" '{print $2}' | tr -d '\n') + tmp_names+=("$touchpad_attr_name") + done + + # Remove duplicates + for elem in "${tmp_names[@]}"; do + if [[ ! " ${touchpad_attr_names[*]} " =~ $elem ]]; then + touchpad_attr_names+=("$elem") + fi + done + + # Create evdev entries + for i in "${!keyboard_attr_names[@]}"; do keyboard_devlinks+=("\"/dev/keyboard$i\""); done + for i in "${!mouse_attr_names[@]}"; do mouse_devlinks+=("\"/dev/mouse$i\""); done + for i in "${!touchpad_attr_names[@]}"; do touchpad_devlinks+=("\"/dev/touchpad$i\""); done + + # Use udevadm to query misc info (INPUT_KEY, INPUT_SWITCH) + tmp_devs=() + tmp_names=() + for event in "${misc_devices[@]}"; do + read -r -a devlinks <<< "$(udevadm info "$event" | grep "DEVLINKS" | awk -F "=" '{print $2}')" + misc_attr_name=$(udevadm info -a "$event" | grep "ATTRS{name}" | awk -F "==" '{print $2}' | tr -d '\n') + tmp_names+=("$misc_attr_name"); + for dev in "${devlinks[@]}"; do tmp_devs+=("$dev"); done; + done + + # Remove duplicates + for elem in "${tmp_names[@]}"; do + if [[ ! " ${misc_attr_names[*]} " =~ $elem ]]; then + misc_attr_names+=("$elem") + fi + done + for elem in "${tmp_devs[@]}"; do + if [[ ! " ${misc_devlinks[*]} " =~ $elem ]]; then + misc_devlinks+=("$elem") + fi + done + + if $verbose; then + echo -e "Detected keyboard device names:\n${keyboard_attr_names[*]}\n" + echo -e "Detected mouse device names:\n${mouse_attr_names[*]}\n" + echo -e "Detected touchpad device names:\n${touchpad_attr_names[*]}\n" + echo -e "Miscellaneous device names:\n${misc_attr_names[*]}\n" + echo -e "Miscellaneous device links:\n${misc_devlinks[*]}\n" + fi +} + +### USB DEVICE DETECTION ### + +# Function to create USB device entry +add_usb() { + usb_device="$1" + name="$2" + + # Get USB device info + bus=$(echo "$usb_device" | awk '{print $2}') + dev=$(echo "$usb_device" | awk '{print $4}' | tr -d ':') + hostport=$(udevadm info "/dev/bus/usb/$bus/$dev" | grep R: | awk -F "R: " '{print $2}') + hostbus=${bus//00/} + + # Write USB device entry + usb_entry=$(cat << EOF +{ + name="$name"; + hostbus="$hostbus"; + hostport="$hostport"; +} +EOF +) + usb_devices+=("$usb_entry") + if $verbose; then + echo "USB device: $usb_entry" + fi +} +# Search for USB devices using lsusb +detect_usb_devices() { + search_pattern="$1" + group_id="$2" + + # Detect USB devices + usb_devs=() + while IFS= read -r line; do + usb_devs+=("$line") + done <<< "$(lsusb | grep -i "$search_pattern")" + if [ ${#usb_devs[@]} -ge 1 ] && [ "${usb_devs[0]}" != "" ]; then + local n=0 + for i in "${!usb_devs[@]}"; do + read -r -p "Select '${usb_devs[$i]}'? [Y/n] " answer + answer=${answer:-Y} + case $answer in + [Yy]* ) add_usb "${usb_devs[$i]}" "$group_id$n"; n=$((n+1)); continue;; + [Nn]* ) continue;; + * ) echo "Please answer yY or nN.";; + esac + done + fi +} + +### DISK DETECTION ### + +# Detect disks +detect_disks() { + echo "" + lsblk -o NAME,TYPE,SIZE,MODEL -d + read -r -p "Enter the disk device name (default: nvme0n1): " disk_name + disk_name=${disk_name:-nvme0n1} + disk="/dev/$disk_name" + if $verbose; then + echo "disks.disk1.device = { $disk }" + fi +} + +### HW INFO & CONFIG FILE ### + +# Generate extended hardware info +ext_output() { + if [ ! -d hwinfo/ ]; then mkdir -p hwinfo/; fi + lspci -nn >> hwinfo/lspci.txt + lspci_long=$(sudo lspci -nnvvv) + echo "$lspci_long" >> hwinfo/lspci-long.txt + lsusb >> hwinfo/lsusb.txt + lsusb_v=$(sudo lsusb -v) + echo "$lsusb_v" >> hwinfo/lsusb-v.txt + lsusb -t >> hwinfo/lsusb-t.txt + dmi_info=$(sudo dmidecode) + echo "$dmi_info" >> hwinfo/dmidecode.txt + lsblk -o NAME,TYPE,SIZE,MODEL -d > hwinfo/lsblk.txt + udev_db=$(udevadm info --export-db) + echo "$udev_db" >> hwinfo/udevadm.txt + dmesg > hwinfo/dmesg.txt + if [ -f "$CONFIG_FILE" ]; then + cp "$CONFIG_FILE" hwinfo/ + fi + tar -czf hwinfo.tar.gz hwinfo/ + echo "> Extended output files written to hwinfo/ directory." +} + +# Write the hardware configuration file +write_file() { + echo "> Writing hardware configuration file..." + cat << EOF > "$CONFIG_FILE" +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ +# System name +name = "$system_name"; + +# List of system SKUs covered by this configuration +skus = [ + "$system_sku" +]; + +# Host configuration +host = { + kernelConfig.kernelParams = [ + "intel_iommu=on,sm_on" + "iommu=pt" + "acpi_backlight=vendor" + "acpi_osi=linux" + #"module_blacklist=$host_blacklist" + ]; +}; + +# Input devices +input = { + keyboard = { + name = [ + ${keyboard_attr_names[@]} + ]; + evdev = [ + ${keyboard_devlinks[@]} + ]; + }; + + mouse = { + name = [ + ${mouse_attr_names[@]} + ]; + evdev = [ + ${mouse_devlinks[@]} + ]; + }; + + touchpad = { + name = [ + ${touchpad_attr_names[@]} + ]; + evdev = [ + ${touchpad_devlinks[@]} + ]; + }; + + misc = { + name = [ + # ${misc_attr_names[@]} + ]; + evdev = [ + # ${misc_devlinks[@]} + ]; + }; +}; + +# Main disk device +disks = { + disk1.device = "$disk"; +}; + +# Network devices for passthrough to netvm +network = { + pciDevices = [ + ${pci_devices[$NET_ID_1]} + ${pci_devices[$NET_ID_2]} + ]; + kernelConfig = { + # Kernel modules are indicative only, please investigate with lsmod/modinfo + stage1.kernelModules = []; + stage2.kernelModules = [ + ${kernel_modules[$NET_ID_1]} + ${kernel_modules[$NET_ID_2]} + ]; + kernelParams = []; + }; +}; + +# GPU devices for passthrough to guivm +gpu = { + pciDevices = [${pci_devices[$GPU_ID]}]; + kernelConfig = { + # Kernel modules are indicative only, please investigate with lsmod/modinfo + stage1.kernelModules = [ + ${kernel_modules[$GPU_ID]} + ]; + stage2.kernelModules = []; + kernelParams = [ + "earlykms" + ]; + }; +}; + +# Audio device for passthrough to audiovm +audio = { + pciDevices = [ + ${pci_devices["$SND_ID"]} + ]; + kernelConfig = { + # Kernel modules are indicative only, please investigate with lsmod/modinfo + stage1.kernelModules = []; + stage2.kernelModules = [ + ${kernel_modules[$SND_ID]} + ]; + kernelParams = []; + }; +}; + +# USB devices for passthrough +usb = { + internal = [${usb_devices[@]}]; + external = [ + # Add external USB devices here + ]; +}; +} +EOF + + # Format the output file + alejandra --quiet "$CONFIG_FILE" + echo "> File written: $CONFIG_FILE" +} + +### MAIN ### +echo "> Running hardware detection tool..." + +# Default options +verbose=true +if [ $# -eq 0 ]; then + set -- "-s" "-n" "-g" "-a" "-i" "-u" "-d" + verbose=false +fi + +# Run commands +for cmd in "$@"; do + case $cmd in + -s | --sys) + echo "> Scanning system information..." + detect_system_info + continue + ;; + -n | --network) + echo "> Scanning network PCI devices..." + detect_pci_devices "network" $NET_ID_1 + detect_pci_devices "ethernet" $NET_ID_2 + continue + ;; + -g | --gpu) + echo "> Scanning GPU PCI devices..." + detect_pci_devices "vga" $GPU_ID + continue + ;; + -a | --audio) + echo "> Scanning audio PCI devices..." + detect_pci_devices "audio" $SND_ID + continue + ;; + -p | --pci) + echo "> Scanning PCI devices..." + detect_pci_devices + continue + ;; + -i | --input) + echo "> Scanning input devices..." + detect_input_devices + continue + ;; + -u | --usb) + echo "> Scanning USB devices..." + detect_usb_devices "cam" "cam" + detect_usb_devices "fingerprint\|fprint\|biometric" "fpr" + detect_usb_devices "gps\|gnss" "gps" + continue + ;; + -d | --disk) + echo "> Scanning disk devices..." + detect_disks + continue + ;; + -e | --ext) + echo "> Searching for more hardware info..." + ext_output + continue + ;; + -h | --help) + usage + exit 0 + ;; + *) + usage + exit 1 + ;; + esac +done + +if ! $verbose; then + write_file + echo "> Searching for more hardware info..." + ext_output + exit 0 +fi \ No newline at end of file diff --git a/packages/hart-software-services/0001-Workaround-for-a-compilation-issue.patch b/packages/hart-software-services/0001-Workaround-for-a-compilation-issue.patch new file mode 100644 index 000000000..6ae109f5c --- /dev/null +++ b/packages/hart-software-services/0001-Workaround-for-a-compilation-issue.patch @@ -0,0 +1,16 @@ +diff --git a/application/rules.mk b/application/rules.mk +index ff3905e..f663089 100644 +--- a/application/rules.mk ++++ b/application/rules.mk +@@ -113,7 +113,7 @@ ifdef CONFIG_CC_STACKPROTECTOR_STRONG + # CORE_CFLAGS+=-fstack-clash-protection # currently does nothing on RISC-V + else + $(info INFO: NOTICE: enabling -flto (which means stack protection is disabled)) +- OPT-y+=-flto=auto -ffat-lto-objects -fcompare-debug -fno-stack-protector ++ OPT-y+=-flto=auto -ffat-lto-objects -fno-stack-protector + endif + + ############################################################################## +-- +2.42.0 + diff --git a/packages/hart-software-services/default.nix b/packages/hart-software-services/default.nix index 785ebf322..9967fef93 100644 --- a/packages/hart-software-services/default.nix +++ b/packages/hart-software-services/default.nix @@ -5,50 +5,50 @@ lib, python3, stdenv, -}: let +}: +let version = "v2022.09"; in - stdenv.mkDerivation ( - { - pname = "hart-software-services"; - inherit version; - - src = fetchFromGitHub { - owner = "polarfire-soc"; - repo = "hart-software-services"; - rev = version; - sha256 = "sha256-j/nda7//CjJW09zt/YrBy6h+q+VKE5t/ueXxDzwVWQ0="; - }; - - depsBuildBuild = [ - python3 - ]; - - configurePhase = '' - runHook preConfigure - - cp boards/mpfs-icicle-kit-es/def_config .config - - runHook postConfigure - ''; - - makeFlags = [ - "V=1" - "BOARD=mpfs-icicle-kit-es" - "PLATFORM_RISCV_ABI=lp64d" - "PLATFORM_RISCV_ISA=rv64imadc_zicsr_zifencei" - ]; - - installPhase = '' - runHook preInstall - - mkdir -p $out - cp Default/*.elf Default/*.bin $out/ - - runHook postInstall - ''; - } - // lib.optionalAttrs (stdenv.buildPlatform.system != stdenv.hostPlatform.system) { - CROSS_COMPILE = stdenv.cc.targetPrefix; - } - ) +stdenv.mkDerivation ( + { + pname = "hart-software-services"; + inherit version; + + src = fetchFromGitHub { + owner = "polarfire-soc"; + repo = "hart-software-services"; + rev = version; + sha256 = "sha256-j/nda7//CjJW09zt/YrBy6h+q+VKE5t/ueXxDzwVWQ0="; + }; + + patches = [ ./0001-Workaround-for-a-compilation-issue.patch ]; + depsBuildBuild = [ python3 ]; + + configurePhase = '' + runHook preConfigure + + cp boards/mpfs-icicle-kit-es/def_config .config + + runHook postConfigure + ''; + + makeFlags = [ + "V=1" + "BOARD=mpfs-icicle-kit-es" + "PLATFORM_RISCV_ABI=lp64d" + "PLATFORM_RISCV_ISA=rv64imadc_zicsr_zifencei" + ]; + + installPhase = '' + runHook preInstall + + mkdir -p $out + cp Default/*.elf Default/*.bin $out/ + + runHook postInstall + ''; + } + // lib.optionalAttrs (stdenv.buildPlatform.system != stdenv.hostPlatform.system) { + CROSS_COMPILE = stdenv.cc.targetPrefix; + } +) diff --git a/packages/icon-pack/default.nix b/packages/icon-pack/default.nix new file mode 100644 index 000000000..d9f9d419b --- /dev/null +++ b/packages/icon-pack/default.nix @@ -0,0 +1,49 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# This package contains only the assets that we need from papirus-icon-theme, +# so we don't include the entire theme in the distribution. +{ + lib, + runCommand, + papirus-icon-theme, +}: +let + icons = [ + "chromium.svg" + "distributor-logo-android.svg" + "distributor-logo-windows.svg" + "document-viewer.svg" + "element-desktop.svg" + "firefox.svg" + "microsoft-365.svg" + "ms-outlook.svg" + "preferences-system-network.svg" + "slack.svg" + "system-lock-screen.svg" + "system-log-out.svg" + "system-reboot.svg" + "system-shutdown.svg" + "system-suspend-hibernate.svg" + "system-suspend.svg" + "teams-for-linux.svg" + "thorium-browser.svg" + "utilities-terminal.svg" + "yast-vpn.svg" + ]; +in +runCommand "icon-pack" + { + # Preserve Papirus license + meta.license = papirus-icon-theme.meta.license; + } + '' + mkdir -p $out + # All SVGs are located inside 64x64, all other sizes are symlinks. + + ${lib.concatStringsSep "\n" ( + map (icon: '' + cp ${papirus-icon-theme}/share/icons/Papirus/64x64/apps/${icon} $out/ + '') icons + )} + '' diff --git a/packages/installer/default.nix b/packages/installer/default.nix index 554dd014e..18fe555d6 100644 --- a/packages/installer/default.nix +++ b/packages/installer/default.nix @@ -1,23 +1,19 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 { - bash, - imagePath, - substituteAll, + coreutils, + util-linux, + hwinfo, + writeShellApplication, + zstd, }: -substituteAll { - dir = "bin"; - isExecutable = true; - - pname = "ghaf-installer"; - src = ./ghaf-installer.sh; - inherit imagePath; - - buildInputs = [ - bash +writeShellApplication { + name = "ghaf-installer"; + runtimeInputs = [ + coreutils + util-linux + zstd + hwinfo ]; - - postInstall = '' - patchShebangs $out/bin/ghaf-installer.sh - ''; + text = builtins.readFile ./ghaf-installer.sh; } diff --git a/packages/installer/ghaf-installer.sh b/packages/installer/ghaf-installer.sh index b4632c27a..4b5f693c0 100755 --- a/packages/installer/ghaf-installer.sh +++ b/packages/installer/ghaf-installer.sh @@ -1,7 +1,40 @@ #!/usr/bin/env bash # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 + +if [ "$EUID" -ne 0 ]; then + echo "Please run as root" + exit +fi + +# Make sure $IMG_PATH env is set +if [ -z "$IMG_PATH" ]; then + echo "IMG_PATH is not set!" + exit +fi + +usage() { + echo " " + echo "Usage: $(basename "$0") [-w]" + echo " -w Wipe only" + exit 1 +} + +WIPE_ONLY=false + +while getopts "w" opt; do + case $opt in + w) + WIPE_ONLY=true + ;; + ?) + usage + ;; + esac +done + clear + cat <<"EOF" ,----.. ,---, / / \ ,--.' | .--., @@ -20,15 +53,66 @@ EOF echo "Welcome to Ghaf installer!" -echo "To install image choose path to the device on which image will be installed." +echo "To install image or wipe installed image choose path to the device." + +hwinfo --disk --short + +while true; do + read -r -p "Device name [e.g. /dev/nvme0n1]: " DEVICE_NAME + + if [ ! -d "/sys/block/$(basename "$DEVICE_NAME")" ]; then + echo "Device not found!" + continue + fi + + # Check if removable + if [ "$(cat "/sys/block/$(basename "$DEVICE_NAME")/removable")" != "0" ]; then + read -r -p "Device provided is removable, do you want to continue? [y/N] " response + case "$response" in + [yY][eE][sS] | [yY]) + break + ;; + *) + continue + ;; + esac + fi + + break +done + +echo "Installing/Deleting Ghaf on $DEVICE_NAME" +read -r -p 'Do you want to continue? [y/N] ' response + +case "$response" in +[yY][eE][sS] | [yY]) ;; +*) + echo "Exiting..." + exit + ;; +esac -lsblk -read -r -p "Device name [e.g. /dev/nvme0n1]: " DEVICE_NAME +echo "Wiping device..." +# Wipe any possible ZFS leftovers from previous installations +# Set sector size to 512 bytes +SECTOR=512 +# 10 MiB in 512-byte sectors +MIB_TO_SECTORS=20480 +# Disk size in 512-byte sectors +SECTORS=$(blockdev --getsz "$DEVICE_NAME") +# Wipe first 10MiB of disk +dd if=/dev/zero of="$DEVICE_NAME" bs="$SECTOR" count="$MIB_TO_SECTORS" conv=fsync status=none +# Wipe last 10MiB of disk +dd if=/dev/zero of="$DEVICE_NAME" bs="$SECTOR" count="$MIB_TO_SECTORS" seek="$((SECTORS - MIB_TO_SECTORS))" conv=fsync status=none +echo "Wipe done." -read -r -p 'WARNING: Next command will destroy all previous data from your device, press Enter to proceed. ' +if [ "$WIPE_ONLY" = true ]; then + echo "Wipe only option selected. Exiting..." + echo "Please remove the installation media and reboot" + exit +fi echo "Installing..." -dd if=@imagePath@ of="${DEVICE_NAME}" bs=32M status=progress +zstdcat "$IMG_PATH" | dd of="$DEVICE_NAME" bs=32M status=progress -echo "" echo "Installation done. Please remove the installation media and reboot" diff --git a/packages/kernel-hardening-checker/default.nix b/packages/kernel-hardening-checker/default.nix index dedd96cff..c47fb0ba1 100644 --- a/packages/kernel-hardening-checker/default.nix +++ b/packages/kernel-hardening-checker/default.nix @@ -1,9 +1,6 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ - python3Packages, - fetchFromGitHub, -}: +{ python3Packages, fetchFromGitHub }: python3Packages.buildPythonApplication rec { pname = "kernel-hardening-checker"; version = "0.6.1-git${src.rev}"; diff --git a/packages/kernel/default.nix b/packages/kernel/default.nix index 81ce5cd1f..d958ad330 100644 --- a/packages/kernel/default.nix +++ b/packages/kernel/default.nix @@ -6,78 +6,92 @@ config, pkgs, lib, -}: { - kernelPatches ? [], +}: +{ + kernelPatches ? [ ], config_baseline, host_build ? false, -}: let +}: +let kernel_package = pkgs.linux_latest; version = "${kernel_package.version}-ghaf-hardened"; modDirVersion = version; - base_kernel = - pkgs.linuxManualConfig rec - { - inherit (kernel_package) src; - inherit version modDirVersion kernelPatches; - /* + base_kernel = pkgs.linuxManualConfig rec { + inherit (kernel_package) src; + inherit version modDirVersion kernelPatches; + /* NixOS required (asserted) kernel features to comply with no import from derivation. For the actual kernel build these config options must come via the kernel config_baseline argument - */ - config = { - CONFIG_DEVTMPFS = "y"; - CONFIG_CGROUPS = "y"; - CONFIG_INOTIFY_USER = "y"; - CONFIG_SIGNALFD = "y"; - CONFIG_TIMERFD = "y"; - CONFIG_EPOLL = "y"; - CONFIG_NET = "y"; - CONFIG_SYSFS = "y"; - CONFIG_PROC_FS = "y"; - CONFIG_FHANDLE = "y"; - CONFIG_CRYPTO_USER_API_HASH = "y"; - CONFIG_CRYPTO_HMAC = "y"; - CONFIG_CRYPTO_SHA256 = "y"; - CONFIG_DMIID = "y"; - CONFIG_AUTOFS_FS = "y"; - CONFIG_TMPFS_POSIX_ACL = "y"; - CONFIG_TMPFS_XATTR = "y"; - CONFIG_SECCOMP = "y"; - CONFIG_TMPFS = "y"; - CONFIG_BLK_DEV_INITRD = "y"; - CONFIG_EFI_STUB = "y"; - CONFIG_MODULES = "y"; - CONFIG_BINFMT_ELF = "y"; - CONFIG_UNIX = "y"; - }; - configfile = config_baseline; + */ + config = { + CONFIG_DEVTMPFS = "y"; + CONFIG_CGROUPS = "y"; + CONFIG_INOTIFY_USER = "y"; + CONFIG_SIGNALFD = "y"; + CONFIG_TIMERFD = "y"; + CONFIG_EPOLL = "y"; + CONFIG_NET = "y"; + CONFIG_SYSFS = "y"; + CONFIG_PROC_FS = "y"; + CONFIG_FHANDLE = "y"; + CONFIG_CRYPTO_USER_API_HASH = "y"; + CONFIG_CRYPTO_HMAC = "y"; + CONFIG_CRYPTO_SHA256 = "y"; + CONFIG_DMIID = "y"; + CONFIG_AUTOFS_FS = "y"; + CONFIG_TMPFS_POSIX_ACL = "y"; + CONFIG_TMPFS_XATTR = "y"; + CONFIG_SECCOMP = "y"; + CONFIG_TMPFS = "y"; + CONFIG_BLK_DEV_INITRD = "y"; + CONFIG_EFI_STUB = "y"; + CONFIG_MODULES = "y"; + CONFIG_BINFMT_ELF = "y"; + CONFIG_UNIX = "y"; }; + configfile = config_baseline; + }; - generic_host_configs = ../../modules/common/hardware/x86_64-generic/kernel/host/configs; - generic_guest_configs = ../../modules/common/hardware/x86_64-generic/kernel/guest/configs; + generic_host_configs = ../../modules/hardware/x86_64-generic/kernel/host/configs; + generic_guest_configs = ../../modules/hardware/x86_64-generic/kernel/guest/configs; # TODO: refactor - do we yet have any X1 specific host kernel configuration options? # - we could add a configuration fragment for host debug via usb-ethernet-adapter(s) kernel_features = - lib.optionals config.ghaf.host.kernel.hardening.virtualization.enable ["${generic_host_configs}/virtualization.config"] - ++ lib.optionals config.ghaf.host.kernel.hardening.networking.enable ["${generic_host_configs}/networking.config"] - ++ lib.optionals config.ghaf.host.kernel.hardening.usb.enable ["${generic_host_configs}/usb.config"] - ++ lib.optionals config.ghaf.host.kernel.hardening.inputdevices.enable ["${generic_host_configs}/user-input-devices.config"] - ++ lib.optionals config.ghaf.host.kernel.hardening.debug.enable ["${generic_host_configs}/debug.config"] - ++ lib.optionals (config.ghaf.guest.kernel.hardening.enable && !host_build) ["${generic_guest_configs}/guest.config"] - ++ lib.optionals (config.ghaf.guest.kernel.hardening.graphics.enable && !host_build) ["${generic_guest_configs}/display-gpu.config"]; + lib.optionals config.ghaf.host.kernel.hardening.virtualization.enable [ + "${generic_host_configs}/virtualization.config" + ] + ++ lib.optionals config.ghaf.host.kernel.hardening.networking.enable [ + "${generic_host_configs}/networking.config" + ] + ++ lib.optionals config.ghaf.host.kernel.hardening.usb.enable [ + "${generic_host_configs}/usb.config" + ] + ++ lib.optionals config.ghaf.host.kernel.hardening.inputdevices.enable [ + "${generic_host_configs}/user-input-devices.config" + ] + ++ lib.optionals config.ghaf.host.kernel.hardening.debug.enable [ + "${generic_host_configs}/debug.config" + ] + ++ lib.optionals (config.ghaf.guest.kernel.hardening.enable && !host_build) [ + "${generic_guest_configs}/guest.config" + ] + ++ lib.optionals (config.ghaf.guest.kernel.hardening.graphics.enable && !host_build) [ + "${generic_guest_configs}/display-gpu.config" + ]; kernel = - if lib.length kernel_features > 0 - then + if lib.length kernel_features > 0 then base_kernel.overrideAttrs (_old: { inherit kernel_features; postConfigure = '' ./scripts/kconfig/merge_config.sh -O $buildRoot $buildRoot/.config $kernel_features; ''; }) - else base_kernel; + else + base_kernel; in - kernel +kernel diff --git a/packages/make-checks/default.nix b/packages/make-checks/default.nix new file mode 100644 index 000000000..6146b10f6 --- /dev/null +++ b/packages/make-checks/default.nix @@ -0,0 +1,69 @@ +# Copyright 2020 Jonas Chevalier +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# Shamelessly derived from https://github.com/numtide/nixpkgs-unfree/blob/main/ci.sh +# +# Check that all of the projects can be evaluated. +# This does not build any packages or run any tests, just evaluates the flake packages. +{ + writeShellApplication, + nix-eval-jobs, + jq, + ... +}: +writeShellApplication { + name = "make-checks"; + runtimeInputs = [ + nix-eval-jobs + jq + ]; + text = '' + args=( + "$@" + --accept-flake-config + --gc-roots-dir gcroot + #--max-memory-size "3072" #allow users to set this themselves in extra params if needed + --option allow-import-from-derivation false + --force-recurse + --flake .#checks + ) + + if [[ -n "''${GITHUB_STEP_SUMMARY-}" ]]; then + log() { + #Print to the Summary + echo "$*" >> "$GITHUB_STEP_SUMMARY" + #Print to the inline log + echo "$*" + } + else + log() { + echo "$*" + } + fi + + echo "starting..." + echo "Grab some Coffee, this will take a while..." + + retError=0 + + for job in $(nix-eval-jobs "''${args[@]}" 2>/dev/null | jq -r '. | @base64'); do + job=$(echo "$job" | base64 -d) + attr=$(echo "$job" | jq -r .attr) + error=$(echo "$job" | jq -r .error) + if [[ $error != null ]]; then + log "### ❌ $attr" + log + log "
Eval error:
"
+        log "$error"
+        log "
" + retError=1 + else + log "### ✅ $attr" + fi + done + + #TODO: should we remove gcroot? + exit "$retError" + ''; +} diff --git a/packages/mitmweb-ui/default.nix b/packages/mitmweb-ui/default.nix new file mode 100644 index 000000000..66c902303 --- /dev/null +++ b/packages/mitmweb-ui/default.nix @@ -0,0 +1,51 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + stdenvNoCC, + pkgs, + lib, + ... +}: +let + waypipePort = 1100; # TODO: remove hardcoded port number + idsvmIP = "ids-vm"; + mitmwebUI = pkgs.writeShellScript "mitmweb-ui" '' + # Create ssh-tunnel between chromium-vm and ids-vm + ${pkgs.openssh}/bin/ssh -i /run/waypipe-ssh/id_ed25519 \ + -o StrictHostKeyChecking=no \ + -t ghaf@chromium-vm \ + ${pkgs.openssh}/bin/ssh -M -S /tmp/control_socket \ + -f -N -L 8081:localhost:8081 ghaf@${idsvmIP} + # TODO: check pipe creation failures + + # Launch chromium application and open mitmweb page + ${pkgs.openssh}/bin/ssh -i /run/waypipe-ssh/id_ed25519 -o StrictHostKeyChecking=no chromium-vm \ + ${pkgs.waypipe}/bin/waypipe --border=#ff5733,5 --vsock -s ${toString waypipePort} server \ + chromium --enable-features=UseOzonePlatform --ozone-platform=wayland \ + http://localhost:8081 + + # Use the control socket to close the ssh tunnel between chromium-vm and ids-vm + ${pkgs.openssh}/bin/ssh -i /run/waypipe-ssh/id_ed25519 \ + -o StrictHostKeyChecking=no \ + -t ghaf@chromium-vm \ + ${pkgs.openssh}/bin/ssh -q -S /tmp/control_socket -O exit ghaf@${idsvmIP} + ''; +in +stdenvNoCC.mkDerivation { + name = "mitmweb-ui"; + + phases = [ "installPhase" ]; + + installPhase = '' + mkdir -p $out/bin + cp ${mitmwebUI} $out/bin/mitmweb-ui + ''; + + meta = with lib; { + description = "Script to launch Chromium to open mitmweb interface using ssh-tunneling and authentication."; + platforms = [ + "x86_64-linux" + "aarch64-linux" + ]; + }; +} diff --git a/packages/nm-launcher/default.nix b/packages/nm-launcher/default.nix index d624dd2c1..f41f570f4 100644 --- a/packages/nm-launcher/default.nix +++ b/packages/nm-launcher/default.nix @@ -1,7 +1,6 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 { - lib, # NOTE: By default networkmanagerapplet and openssh are taken from the same # callPackage set! This means they will be both taken from the same # /nix/store, so it is recommended to override the networkmanagerapplet @@ -9,6 +8,7 @@ networkmanagerapplet, openssh, writeShellApplication, + lib, ... }: writeShellApplication { @@ -18,22 +18,21 @@ writeShellApplication { export DBUS_SESSION_BUS_ADDRESS=unix:path=/tmp/ssh_session_dbus.sock export DBUS_SYSTEM_BUS_ADDRESS=unix:path=/tmp/ssh_system_dbus.sock ${openssh}/bin/ssh -M -S /tmp/control_socket \ - -f -N -q ghaf@192.168.100.1 \ + -f -N -q ghaf@net-vm \ -i /run/waypipe-ssh/id_ed25519 \ -o StrictHostKeyChecking=no \ + -o UserKnownHostsFile=/dev/null \ -o StreamLocalBindUnlink=yes \ -o ExitOnForwardFailure=yes \ -L /tmp/ssh_session_dbus.sock:/run/user/1000/bus \ -L /tmp/ssh_system_dbus.sock:/run/dbus/system_bus_socket ${networkmanagerapplet}/bin/nm-connection-editor # Use the control socket to close the ssh tunnel. - ${openssh}/bin/ssh -q -S /tmp/control_socket -O exit ghaf@192.168.100.1 + ${openssh}/bin/ssh -q -S /tmp/control_socket -O exit ghaf@net-vm ''; - meta = with lib; { + meta = { description = "Script to launch nm-connection-editor to configure network of netvm using D-Bus over SSH."; - platforms = [ - "x86_64-linux" - ]; + platforms = lib.platforms.linux; }; } diff --git a/targets/lenovo-x1/openPdf.nix b/packages/openPdf/default.nix similarity index 63% rename from targets/lenovo-x1/openPdf.nix rename to packages/openPdf/default.nix index 18d1a4bf5..37b123bc1 100644 --- a/targets/lenovo-x1/openPdf.nix +++ b/packages/openPdf/default.nix @@ -2,34 +2,39 @@ # SPDX-License-Identifier: Apache-2.0 # { - pkgs, + writeShellApplication, + dnsutils, + openssh, sshKeyPath, ... }: # The openpdf script is executed by the xdg handler from the chromium-vm # It reads the file path, copies it from chromium-vm to zathura-vm and opens it there -pkgs.writeShellApplication { +writeShellApplication { name = "openPdf"; - runtimeInputs = [pkgs.dnsutils pkgs.openssh]; + runtimeInputs = [ + dnsutils + openssh + ]; text = '' read -r sourcepath filename=$(basename "$sourcepath") zathurapath="/var/tmp/$filename" - chromiumip=$(dig +short chromium-vm.ghaf | head -1) + chromiumip=$(dig +short chromium-vm | head -1) if [[ "$chromiumip" != "$REMOTE_ADDR" ]]; then - echo "Open PDF request received from $REMOTE_ADDR, but it is only permitted for chromium-vm.ghaf with IP $chromiumip" + echo "Open PDF request received from $REMOTE_ADDR, but it is only permitted for chromium-vm with IP $chromiumip" exit 0 fi echo "Copying $sourcepath from $REMOTE_ADDR to $zathurapath in zathura-vm" - scp -i ${sshKeyPath} -o StrictHostKeyChecking=no "$REMOTE_ADDR":"$sourcepath" zathura-vm.ghaf:"$zathurapath" + scp -i ${sshKeyPath} -o StrictHostKeyChecking=no "$REMOTE_ADDR":"$sourcepath" zathura-vm:"$zathurapath" echo "Opening $zathurapath in zathura-vm" - ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no zathura-vm.ghaf run-waypipe zathura "$zathurapath" + ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no zathura-vm run-waypipe zathura "'$zathurapath'" echo "Deleting $zathurapath in zathura-vm" - ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no zathura-vm.ghaf rm -f "$zathurapath" + ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no zathura-vm rm -f "$zathurapath" ''; } diff --git a/packages/powercontrol/default.nix b/packages/powercontrol/default.nix index 189503420..664867dde 100644 --- a/packages/powercontrol/default.nix +++ b/packages/powercontrol/default.nix @@ -5,81 +5,74 @@ openssh, stdenv, writeShellScript, -}: let +}: +let systemctl = "/run/current-system/systemd/bin/systemctl"; busName = "org.freedesktop.login1"; - makeSystemCtlPowerActionViaSsh = { - hostAddress, - sshKeyPath, - method, - }: - writeShellScript - "${method}-host" - '' ${openssh}/bin/ssh \ - -i ${sshKeyPath} \ + makeSystemCtlPowerActionViaSsh = + { + hostAddress, + privateSshKeyPath, + method, + }: + writeShellScript "${method}-host" '' + ${openssh}/bin/ssh \ + -i ${privateSshKeyPath} \ -o StrictHostKeyChecking=no \ ghaf@${hostAddress} \ ${systemctl} ${method}''; in - stdenv.mkDerivation { - name = "powercontrol"; +stdenv.mkDerivation { + name = "powercontrol"; - makePowerOffCommand = { - hostAddress, - sshKeyPath, - }: - makeSystemCtlPowerActionViaSsh { - inherit hostAddress sshKeyPath; - method = "poweroff"; - }; + makePowerOffCommand = + { hostAddress, privateSshKeyPath }: + makeSystemCtlPowerActionViaSsh { + inherit hostAddress privateSshKeyPath; + method = "poweroff"; + }; - makeRebootCommand = { - hostAddress, - sshKeyPath, - }: - makeSystemCtlPowerActionViaSsh { - inherit hostAddress sshKeyPath; - method = "reboot"; - }; + makeRebootCommand = + { hostAddress, privateSshKeyPath }: + makeSystemCtlPowerActionViaSsh { + inherit hostAddress privateSshKeyPath; + method = "reboot"; + }; - makeSuspendCommand = { - hostAddress, - sshKeyPath, - }: - makeSystemCtlPowerActionViaSsh { - inherit hostAddress sshKeyPath; - method = "suspend"; - }; + makeSuspendCommand = + { hostAddress, privateSshKeyPath }: + makeSystemCtlPowerActionViaSsh { + inherit hostAddress privateSshKeyPath; + method = "suspend"; + }; - makeHibernateCommand = { - hostAddress, - sshKeyPath, - }: - makeSystemCtlPowerActionViaSsh { - inherit hostAddress sshKeyPath; - method = "hibernate"; - }; + makeHibernateCommand = + { hostAddress, privateSshKeyPath }: + makeSystemCtlPowerActionViaSsh { + inherit hostAddress privateSshKeyPath; + method = "hibernate"; + }; - polkitExtraConfig = '' - polkit.addRule(function(action, subject) { - if ((subject.user == "ghaf") && - (action.id == "${busName}.power-off" || - action.id == "${busName}.power-off-multiple-sessions" || - action.id == "${busName}.reboot" || - action.id == "${busName}.reboot-multiple-sessions" || - action.id == "${busName}.suspend" || - action.id == "${busName}.suspend-multiple-sessions" || - action.id == "${busName}.hibernate" || - action.id == "${busName}.hibernate-multiple-sessions") - ) { - return polkit.Result.YES; - } - }); - ''; + polkitExtraConfig = '' + polkit.addRule(function(action, subject) { + if ((subject.user == "ghaf") && + (action.id == "${busName}.power-off" || + action.id == "${busName}.power-off-multiple-sessions" || + action.id == "${busName}.reboot" || + action.id == "${busName}.reboot-multiple-sessions" || + action.id == "${busName}.suspend" || + action.id == "${busName}.suspend-multiple-sessions" || + action.id == "${busName}.hibernate" || + action.id == "${busName}.hibernate-multiple-sessions") + ) { + return polkit.Result.YES; + } + }); + ''; - meta = { - description = "Scripts for host power control"; - platforms = lib.platforms.linux; - }; - } + meta = { + description = "Scripts for host power control"; + platforms = lib.platforms.linux; + }; +} diff --git a/packages/powercontrol/png-icons.nix b/packages/powercontrol/png-icons.nix deleted file mode 100644 index e3a7d0669..000000000 --- a/packages/powercontrol/png-icons.nix +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - adwaita-icon-theme, - librsvg, - stdenv, -}: let - shutdownIconName = "system-shutdown-symbolic"; - rebootIconName = "system-reboot-symbolic"; - - iconColor = "white"; - - changeColorCcs = "path { fill: ${iconColor} !important; }"; - changeColorCcsPath = "$out/bin/color.css"; - - getIconPath = {iconName}: "bin/${iconName}.png"; -in - stdenv.mkDerivation { - name = "powercontrol-png-icons"; - - phases = ["installPhase"]; - - relativeShutdownIconPath = getIconPath {iconName = shutdownIconName;}; - relativeRebootIconPath = getIconPath {iconName = rebootIconName;}; - - installPhase = let - adwaitaRoot = "${adwaita-icon-theme}/share/icons/Adwaita/symbolic/actions/"; - convertIconCommand = {iconName}: let - outIconPath = getIconPath {inherit iconName;}; - in "${librsvg}/bin/rsvg-convert --stylesheet=${changeColorCcsPath} ${adwaitaRoot}/${iconName}.svg -o $out/${outIconPath}"; - - shutdown = convertIconCommand {iconName = shutdownIconName;}; - reboot = convertIconCommand {iconName = rebootIconName;}; - in '' - mkdir -p $out/bin; - - echo '${changeColorCcs}' > ${changeColorCcsPath}; - - ${shutdown}; - ${reboot}; - ''; - - meta = { - description = "Icons for power control"; - inherit (adwaita-icon-theme.meta) license; - inherit (librsvg.meta) platforms; - }; - } diff --git a/packages/qemuqmp/default.nix b/packages/qemuqmp/default.nix new file mode 100644 index 000000000..0979b8b50 --- /dev/null +++ b/packages/qemuqmp/default.nix @@ -0,0 +1,26 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + python3Packages, + fetchPypi, + lib, +}: +python3Packages.buildPythonPackage rec { + pname = "qemu.qmp"; + version = "0.0.3"; + + src = fetchPypi { + inherit pname version; + sha256 = "sha256-y8iPvMEV7pQ9hER9FyxkLaEgIgRRQWwvYhrPM98eEBA="; + }; + + pyproject = true; + + nativeBuildInputs = [ python3Packages.setuptools-scm ]; + + meta = { + homepage = "https://www.qemu.org/"; + description = "QEMU Monitor Protocol library"; + license = lib.licenses.lgpl2Plus; + }; +} diff --git a/packages/ssh-keys-helper/default.nix b/packages/ssh-keys-helper/default.nix new file mode 100644 index 000000000..e0dabb719 --- /dev/null +++ b/packages/ssh-keys-helper/default.nix @@ -0,0 +1,16 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ pkgs, config }: +{ + getAuthKeysSource = { + source = + let + script = pkgs.writeShellScriptBin config.ghaf.security.sshKeys.getAuthKeysFileName '' + [[ "$1" != "ghaf" ]] && exit 0 + ${pkgs.coreutils}/bin/cat ${config.ghaf.security.sshKeys.waypipeSshPublicKeyFile} + ''; + in + "${script}/bin/${config.ghaf.security.sshKeys.getAuthKeysFileName}"; + mode = "0555"; + }; +} diff --git a/packages/vhotplug/default.nix b/packages/vhotplug/default.nix new file mode 100644 index 000000000..890bfaa13 --- /dev/null +++ b/packages/vhotplug/default.nix @@ -0,0 +1,29 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + python3Packages, + pkgs, + fetchFromGitHub, +}: +let + qemuqmp = pkgs.callPackage ../qemuqmp { }; +in +python3Packages.buildPythonApplication rec { + pname = "vhotplug"; + version = "0.1"; + + propagatedBuildInputs = [ + python3Packages.pyudev + python3Packages.psutil + qemuqmp + ]; + + doCheck = false; + + src = fetchFromGitHub { + owner = "tiiuae"; + repo = "vhotplug"; + rev = "fd05361ed893d06cdb5ac4a538c171e4a86b6f5a"; + hash = "sha256-6fl5xeSpcIIBKn3dZUAEHiNRRpn9LbYC4Imap5KBH2M="; + }; +} diff --git a/packages/vsockproxy/default.nix b/packages/vsockproxy/default.nix index 287279a8c..c078456ac 100644 --- a/packages/vsockproxy/default.nix +++ b/packages/vsockproxy/default.nix @@ -2,7 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 { fetchFromGitHub, - lib, meson, ninja, stdenv, @@ -10,13 +9,16 @@ stdenv.mkDerivation { name = "vsockproxy"; - depsBuildBuild = [meson ninja]; + depsBuildBuild = [ + meson + ninja + ]; src = fetchFromGitHub { owner = "tiiuae"; repo = "vsockproxy"; - rev = "aad625f9a27ce4c68d9996c65ece8477ace37534"; - sha256 = "sha256-3WgpDlF8oIdlgwkvl7TPR6WAh+qk0mowzuYiPY0rwaU="; + rev = "851e995b4c24a776f78d56310010e4e29456921c"; + sha256 = "sha256-fyawskwts4OIBshGDeh5ANeBCEm3h5AyHCyhwfxgP14="; }; installPhase = '' @@ -28,7 +30,7 @@ stdenv.mkDerivation { runHook postInstall ''; - meta = with lib; { + meta = { description = "vsockproxy"; platforms = [ "x86_64-linux" diff --git a/packages/wifi-connector/default.nix b/packages/wifi-connector/default.nix deleted file mode 100644 index f4d9f74fa..000000000 --- a/packages/wifi-connector/default.nix +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - lib, - writeShellApplication, - useNmcli ? false, - ... -}: -writeShellApplication { - name = "wifi-connector"; - - text = - '' - # Check if the script is run as root - if [ "$EUID" -ne 0 ]; then - echo "Please run this script as root or with sudo." - exit 1 - fi - - while getopts ":ds:p:" opt; do - case $opt in - d) - echo "Disconnecting..." - '' - + lib.optionalString useNmcli '' - CONNECTION=$(nmcli d | grep -w wifi | grep -w connected | awk '{print $4}') - if [ -z "$CONNECTION" ]; then - echo "No active Wi-Fi connection found"; - exit 0; - fi - nmcli con down id "$CONNECTION" - '' - + lib.optionalString (!useNmcli) '' - #Stop any running wpa_supplicant instances - pkill wpa_supplicant - '' - + '' - exit 0 - ;; - s) - SSID=$OPTARG - ;; - p) - PSK=$OPTARG - ;; - \?) - echo "Invalid option: -$OPTARG" >&2 - exit 1 - ;; - :) - echo "Option -$OPTARG does not take an argument." >&2 - exit 1 - ;; - esac - done - - if [ -z "$SSID" ] || [ -z "$PSK" ]; then - echo "Usage: $0 -s -p OR -d to disconnect" - exit 1 - fi - - '' - + lib.optionalString useNmcli '' - #Run nmcli command, get its output; - #split above result with ' as a delimiter and take the second part (devicename) - DEVICE=$(nmcli device wifi connect "$SSID" password "$PSK" | cut -d"'" -f2) - '' - + lib.optionalString (!useNmcli) '' - #Stop any running wpa_supplicant instances - pkill wpa_supplicant - - DEVICE=$(ifconfig | grep wlp | cut -d":" -f1) - - # Create a wpa_supplicant configuration file - cat > ./wpa_supplicant.conf <"$LOCK_FILE" - ${pkgs.util-linux}/bin/flock -w 60 -x 99 || exit 1 + # Lock the script to reuse + LOCK_FILE=/tmp/wifi-signal.lock + exec 99>"$LOCK_FILE" + flock -w 60 -x 99 || exit 1 - # Return the result as json format for waybar and use the control socket to close the ssh tunnel. - trap "${pkgs.openssh}/bin/ssh -q -S /tmp/nmcli_socket -O exit ghaf@${netvm_address} && ${pkgs.coreutils-full}/bin/cat $NETWORK_STATUS_FILE" EXIT + # Return the result as json format for waybar and use the control socket to close the ssh tunnel. + trap 'ssh -q -S /tmp/nmcli_socket -O exit ghaf@net-vm && cat "$NETWORK_STATUS_FILE"' EXIT - # Connect to netvm - ${pkgs.openssh}/bin/ssh -M -S /tmp/nmcli_socket \ - -f -N -q ghaf@${netvm_address} \ - -i /run/waypipe-ssh/id_ed25519 \ - -o StrictHostKeyChecking=no \ - -o StreamLocalBindUnlink=yes \ - -o ExitOnForwardFailure=yes \ - -L /tmp/ssh_session_dbus.sock:/run/user/1000/bus \ - -L /tmp/ssh_system_dbus.sock:/run/dbus/system_bus_socket - signal0=󰤟 - signal1=󰤢 - signal2=󰤥 - signal3=󰤨 - no_signal=󰤭 - # Get IP address of netvm - address=$(${pkgs.networkmanager}/bin/nmcli device show ${wifiDevice} | ${pkgs.gawk}/bin/awk '{ if ($1=="IP4.ADDRESS[1]:") {print $2}}') - # Get signal strength and ssid - connection=($(${pkgs.networkmanager}/bin/nmcli -f IN-USE,SIGNAL,SSID dev wifi | ${pkgs.gawk}/bin/awk '/^\*/{if (NR!=1) {print $2; print $3}}')) - connection[0]=$(if [ -z ''${connection[0]} ]; then echo "-1"; else echo ''${connection[0]}; fi) - # Set the icon of signal level - signal_level=$(if [ ''${connection[0]} -gt 80 ]; then echo $signal3; elif [ ''${connection[0]} -gt 60 ]; then echo $signal2; elif [ ''${connection[0]} -gt 30 ]; then echo $signal1; elif [ ''${connection[0]} -gt 0 ]; then echo signal0; else echo $no_signal; fi) - tooltip=$(if [ -z $address ]; then echo ''${connection[0]}%; else echo $address ''${connection[0]}%; fi) - text=$(if [ -z ''${connection[1]} ]; then echo "No connection"; else echo ''${connection[1]} $signal_level; fi) - # Save the result in json format - RESULT="{\"percentage\":\""''${connection[0]}"\", \"text\":\""$text"\", \"tooltip\":\""$tooltip"\", \"class\":\"1\"}" - echo $RESULT>/tmp/network-status - ${pkgs.util-linux}/bin/flock -u 99 - ''; -in - stdenvNoCC.mkDerivation { - name = "wifi-signal-strength"; - - phases = ["installPhase"]; - - installPhase = '' - mkdir -p $out/bin - cp ${wifiSignalStrength} $out/bin/wifi-signal-strength - ''; - - meta = with lib; { - description = "Script to get wifi data from nmcli to show network of netvm using D-Bus over SSH on Waybar."; - platforms = [ - "x86_64-linux" - ]; - }; - } + # Connect to netvm + ssh -M -S /tmp/nmcli_socket \ + -f -N -q ghaf@net-vm \ + -i /run/waypipe-ssh/id_ed25519 \ + -o StrictHostKeyChecking=no \ + -o UserKnownHostsFile=/dev/null \ + -o StreamLocalBindUnlink=yes \ + -o ExitOnForwardFailure=yes \ + -L /tmp/ssh_session_dbus.sock:/run/user/1000/bus \ + -L /tmp/ssh_system_dbus.sock:/run/dbus/system_bus_socket + signal0="\UF091F" + signal1="\UF0922" + signal2="\UF0925" + signal3="\UF0928" + no_signal="\UF092D" + # Get IP address of netvm + address=$(nmcli device show ${wifiDevice} | awk '{ if ($1=="IP4.ADDRESS[1]:") {print $2}}') + # Get signal strength and ssi + mapfile -t connection < <(nmcli -f IN-USE,SIGNAL,SSID dev wifi | awk '/^\*/{if (NR!=1) {print $2; print $3}}') + connection[0]=$(if [ -z "''${connection[0]}" ]; then echo "-1"; else echo "''${connection[0]}"; fi) + # Set the icon of signal level + signal_level=$(if [ "''${connection[0]}" -gt 80 ]; then echo "''${signal3}"; elif [ "''${connection[0]}" -gt 60 ]; then echo "''${signal2}"; elif [ "''${connection[0]}" -gt 30 ]; then echo "''${signal1}"; elif [ "''${connection[0]}" -gt 0 ]; then echo "''${signal0};" else echo "''${no_signal}"; fi) + tooltip=$(if [ -z "''${address}" ]; then echo "''${connection[0]}%"; else echo "''${address} ''${connection[0]}%"; fi) + text=$(if [ -z "''${connection[1]}" ]; then echo "No connection"; else echo "''${connection[1]} $signal_level"; fi) + # Save the result in json format + RESULT="{\"percentage\":\"''${connection[0]}\", \"text\":\"''${text}\", \"tooltip\":\"''${tooltip}\", \"class\":\"1\"}" + echo -e "$RESULT">/tmp/network-status + flock -u 99 + ''; +} diff --git a/packages/windows-launcher/default.nix b/packages/windows-launcher/default.nix index 5554ccd7c..049cc1016 100644 --- a/packages/windows-launcher/default.nix +++ b/packages/windows-launcher/default.nix @@ -4,167 +4,171 @@ stdenvNoCC, lib, stdenv, - qemu, + qemu_kvm, OVMF, - gnome, + yad, writeShellScript, enableSpice ? false, ... -}: let +}: +let ovmfPrefix = - if stdenv.isx86_64 - then "OVMF" - else if stdenv.isAarch64 - then "AAVMF" - else throw "Unsupported architecture"; - windowsLauncher = - writeShellScript - "windows-launcher" - ('' - IMG_FILE=$1 - ISO_FILE="" - if [ $# -eq 0 ]; then - '' - + lib.optionalString stdenv.isAarch64 '' - echo "Usage: windows-launcher ./Windows11_InsiderPreview_Client_ARM64_en-us_25324.VHDX" - '' - + lib.optionalString stdenv.isx86_64 '' - echo "Usage: windows-launcher ./Win11_22H2_English_x64v2.iso or ./win11.qcow2" - '' - + '' - exit - fi - '' - + lib.optionalString (!enableSpice) '' - if [[ -z "''${WAYLAND_DISPLAY}" ]]; then - echo "Wayland display not found" - exit - fi - '' - + '' - IMG_DIR="$(dirname "$IMG_FILE")" - OVMF_VARS="$IMG_DIR/${ovmfPrefix}_VARS.fd" - OVMF_CODE="$IMG_DIR/${ovmfPrefix}_CODE.fd" + if stdenv.isx86_64 then + "OVMF" + else if stdenv.isAarch64 then + "AAVMF" + else + throw "Unsupported architecture"; + windowsLauncher = writeShellScript "windows-launcher" ( + '' + IMG_FILE=$1 + ISO_FILE="" + if [ $# -eq 0 ]; then + '' + + lib.optionalString stdenv.isAarch64 '' + echo "Usage: windows-launcher ./Windows11_InsiderPreview_Client_ARM64_en-us_25324.VHDX" + '' + + lib.optionalString stdenv.isx86_64 '' + echo "Usage: windows-launcher ./Win11_22H2_English_x64v2.iso or ./win11.qcow2" + '' + + '' + exit + fi + '' + + lib.optionalString (!enableSpice) '' + if [[ -z "''${WAYLAND_DISPLAY}" ]]; then + echo "Wayland display not found" + exit + fi + '' + + '' + IMG_DIR="$(dirname "$IMG_FILE")" + OVMF_VARS="$IMG_DIR/${ovmfPrefix}_VARS.fd" + OVMF_CODE="$IMG_DIR/${ovmfPrefix}_CODE.fd" - if [ ! -f $OVMF_VARS ] || [ ! -f $OVMF_CODE ]; then - cp ${OVMF.fd}/FV/${ovmfPrefix}_VARS.fd $OVMF_VARS - cp ${OVMF.fd}/FV/${ovmfPrefix}_CODE.fd $OVMF_CODE - chmod 644 $OVMF_VARS + if [ ! -f $OVMF_VARS ] || [ ! -f $OVMF_CODE ]; then + cp ${OVMF.fd}/FV/${ovmfPrefix}_VARS.fd $OVMF_VARS + cp ${OVMF.fd}/FV/${ovmfPrefix}_CODE.fd $OVMF_CODE + chmod 644 $OVMF_VARS + fi + '' + + lib.optionalString stdenv.isx86_64 '' + if [[ $1 == *.iso || $1 == *.ISO ]]; then + ISO_FILE=$1 + IMG_FILE="$IMG_DIR/win11.qcow2" + if [ ! -f $IMG_FILE ]; then + ${qemu_kvm}/bin/qemu-img create -f qcow2 $IMG_FILE 64G fi - '' - + lib.optionalString stdenv.isx86_64 '' - if [[ $1 == *.iso || $1 == *.ISO ]]; then - ISO_FILE=$1 - IMG_FILE="$IMG_DIR/win11.qcow2" - if [ ! -f $IMG_FILE ]; then - ${qemu}/bin/qemu-img create -f qcow2 $IMG_FILE 64G - fi - fi - '' - + '' - QEMU_PARAMS=( - "-name \"Windows VM\"" - "-cpu host" - "-enable-kvm" - "-smp 6" - "-m 8G" - "-drive file=$OVMF_CODE,format=raw,if=pflash,readonly=on" - "-drive file=$OVMF_VARS,format=raw,if=pflash" - '' - + lib.optionalString (!enableSpice) '' - "-vga none" - "-device ramfb" - "-device virtio-gpu-pci" - "-nic user,model=virtio" - '' - + lib.optionalString enableSpice '' - "-vga qxl" - "-device virtio-serial-pci" - "-spice port=5900,addr=0.0.0.0,disable-ticketing=on" - "-netdev tap,id=tap-windows,ifname=tap-windows,script=no,downscript=no" - "-device e1000,netdev=tap-windows,mac=02:00:00:03:55:01" - '' - + '' - "-device qemu-xhci" - "-device usb-kbd" - "-device usb-tablet" - '' - + lib.optionalString stdenv.isAarch64 '' - "-M virt,highmem=on,gic-version=max" - "-drive file=$IMG_FILE,format=vhdx,if=none,id=boot" - "-device usb-storage,drive=boot,serial=boot,bootindex=1" - ) - '' - + lib.optionalString stdenv.isx86_64 '' - "-drive file=$IMG_FILE,format=qcow2,if=none,id=boot" - "-device nvme,drive=boot,serial=boot,bootindex=1" - ) + fi + '' + + '' + QEMU_PARAMS=( + "-name \"Windows VM\"" + "-cpu host" + "-enable-kvm" + "-smp 6" + "-m 8G" + "-drive file=$OVMF_CODE,format=raw,if=pflash,readonly=on" + "-drive file=$OVMF_VARS,format=raw,if=pflash" + '' + + lib.optionalString (!enableSpice) '' + "-vga none" + "-device ramfb" + "-device virtio-gpu-pci" + "-nic user,model=virtio" + '' + + lib.optionalString enableSpice '' + "-vga qxl" + "-device virtio-serial-pci" + "-spice port=5900,addr=0.0.0.0,disable-ticketing=on" + "-netdev tap,id=tap-windows,ifname=tap-windows,script=no,downscript=no" + "-device e1000,netdev=tap-windows,mac=02:00:00:03:55:01" + '' + + '' + "-device qemu-xhci" + "-device usb-kbd" + "-device usb-tablet" + '' + + lib.optionalString stdenv.isAarch64 '' + "-M virt,highmem=on,gic-version=max" + "-drive file=$IMG_FILE,format=vhdx,if=none,id=boot" + "-device usb-storage,drive=boot,serial=boot,bootindex=1" + ) + '' + + lib.optionalString stdenv.isx86_64 '' + "-drive file=$IMG_FILE,format=qcow2,if=none,id=boot" + "-device nvme,drive=boot,serial=boot,bootindex=1" + ) - if [ ! -z "$ISO_FILE" ]; then - QEMU_PARAMS+=( - "-drive file=$ISO_FILE,media=cdrom,if=none,id=installcd" - "-device usb-storage,drive=installcd,bootindex=0" - ) - fi - '' - + '' - eval "${qemu}/bin/qemu-system-${stdenv.hostPlatform.qemuArch} ''${QEMU_PARAMS[@]} ''${@:2}" - ''); - windowsLauncherUI = - writeShellScript - "windows-launcher-ui" - ('' - if [[ -z "''${WAYLAND_DISPLAY}" ]]; then - echo "Wayland display not found" - exit - fi + if [ ! -z "$ISO_FILE" ]; then + QEMU_PARAMS+=( + "-drive file=$ISO_FILE,media=cdrom,if=none,id=installcd" + "-device usb-storage,drive=installcd,bootindex=0" + ) + fi + '' + + '' + eval "${qemu_kvm}/bin/qemu-kvm ''${QEMU_PARAMS[@]} ''${@:2}" + '' + ); + windowsLauncherUI = writeShellScript "windows-launcher-ui" ( + '' + if [[ -z "''${WAYLAND_DISPLAY}" ]]; then + echo "Wayland display not found" + exit + fi - CONFIG=~/.config/windows-launcher-ui.conf - if [ -f "$CONFIG" ]; then - source $CONFIG - fi + CONFIG=~/.config/windows-launcher-ui.conf + if [ -f "$CONFIG" ]; then + source $CONFIG + fi - if [ ! -f "$FILE" ]; then - '' - + lib.optionalString stdenv.isAarch64 '' - FILE=`${gnome.zenity}/bin/zenity --file-selection --title="Select Windows VM image (VHDX)"` - '' - + lib.optionalString stdenv.isx86_64 '' - FILE=`${gnome.zenity}/bin/zenity --file-selection --title="Select Windows VM image (QCOW2 or ISO)"` - '' - + '' - if [ ''$? -ne 0 ]; then - exit - else - if [[ $FILE != *.iso && $FILE != *.ISO ]]; then - echo FILE="$FILE" > "$CONFIG" - fi + if [ ! -f "$FILE" ]; then + '' + + lib.optionalString stdenv.isAarch64 '' + FILE=`${yad}/bin/yad --file --title="Select Windows VM image (VHDX)"` + '' + + lib.optionalString stdenv.isx86_64 '' + FILE=`${yad}/bin/yad --file --title="Select Windows VM image (QCOW2 or ISO)"` + '' + + '' + if [ ''$? -ne 0 ]; then + exit + else + if [[ $FILE != *.iso && $FILE != *.ISO ]]; then + echo FILE="$FILE" > "$CONFIG" fi fi + fi - if ! ${windowsLauncher} $FILE; then - ${gnome.zenity}/bin/zenity --error --text="Failed to run Windows VM: $?" - fi - ''); + if ! ${windowsLauncher} $FILE; then + ${yad}/bin/yad --image=gtk-dialog-error --text="Failed to run Windows VM: $?" + fi + '' + ); in - stdenvNoCC.mkDerivation { - name = "windows-launcher"; +stdenvNoCC.mkDerivation { + name = "windows-launcher"; - buildInputs = [gnome.zenity qemu OVMF]; + buildInputs = [ + yad + qemu_kvm + OVMF + ]; - phases = ["installPhase"]; + phases = [ "installPhase" ]; - installPhase = '' - mkdir -p $out/bin - cp ${windowsLauncher} $out/bin/windows-launcher - cp ${windowsLauncherUI} $out/bin/windows-launcher-ui - ''; + installPhase = '' + mkdir -p $out/bin + cp ${windowsLauncher} $out/bin/windows-launcher + cp ${windowsLauncherUI} $out/bin/windows-launcher-ui + ''; - meta = with lib; { - description = "Helper scripts for launching Windows virtual machines using QEMU"; - platforms = [ - "x86_64-linux" - "aarch64-linux" - ]; - }; - } + meta = { + description = "Helper scripts for launching Windows virtual machines using QEMU"; + platforms = [ + "x86_64-linux" + "aarch64-linux" + ]; + }; +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..029f887df --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +[tool.ruff] +line-length = 88 +target-version = "py312" +lint.select = [ "E", "F", "I", "U", "N", "RUF", "A" ] +lint.ignore = [ "E501", "A003"] diff --git a/shell.nix b/shell.nix index 867a3e382..e5368b16b 100644 --- a/shell.nix +++ b/shell.nix @@ -5,10 +5,18 @@ # This file originates from: # https://github.com/nix-community/flake-compat # This file provides backward compatibility to nix < 2.4 clients -{system ? builtins.currentSystem}: let +{ + system ? builtins.currentSystem, +}: +let lock = builtins.fromJSON (builtins.readFile ./flake.lock); - inherit (lock.nodes.flake-compat.locked) owner repo rev narHash; + inherit (lock.nodes.flake-compat.locked) + owner + repo + rev + narHash + ; flake-compat = fetchTarball { url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; @@ -20,4 +28,4 @@ src = ./.; }; in - flake.shellNix +flake.shellNix diff --git a/targets/flake-module.nix b/targets/flake-module.nix index 483f2caa6..ff74abcc3 100644 --- a/targets/flake-module.nix +++ b/targets/flake-module.nix @@ -6,9 +6,10 @@ { imports = [ ./generic-x86_64/flake-module.nix - ./imx8qm-mek/flake-module.nix + ./imx8mp-evk/flake-module.nix ./lenovo-x1-installer/flake-module.nix - ./lenovo-x1/flake-module.nix + ./laptop/flake-module.nix + ./laptop-hw-scan/flake-module.nix ./microchip-icicle-kit/flake-module.nix ./nvidia-jetson-orin/flake-module.nix ./vm/flake-module.nix diff --git a/targets/generic-x86_64/flake-module.nix b/targets/generic-x86_64/flake-module.nix index 71cc549ff..e358890b1 100644 --- a/targets/generic-x86_64/flake-module.nix +++ b/targets/generic-x86_64/flake-module.nix @@ -7,54 +7,66 @@ lib, self, ... -}: let - inherit (inputs) microvm nixos-generators; +}: +let + inherit (inputs) nixos-generators; name = "generic-x86_64"; system = "x86_64-linux"; - generic-x86 = variant: extraModules: let - netvmExtraModules = [ - { - microvm.devices = [ - { - bus = "pci"; - path = "0000:00:14.3"; - } - ]; + generic-x86 = + variant: extraModules: + let + netvmExtraModules = [ + { + microvm.devices = [ + { + bus = "pci"; + path = "0000:00:14.3"; + } + ]; - # For WLAN firmwares - hardware.enableRedistributableFirmware = true; + # For WLAN firmwares + hardware.enableRedistributableFirmware = true; - networking.wireless = { - enable = true; + networking.wireless = { + enable = true; - # networks."SSID_OF_NETWORK".psk = "WPA_PASSWORD"; - }; - } - ]; - hostConfiguration = lib.nixosSystem { - inherit system; - modules = - [ - microvm.nixosModules.host + # networks."SSID_OF_NETWORK".psk = "WPA_PASSWORD"; + }; + services.dnsmasq.settings.dhcp-option = [ + "option:router,192.168.100.1" # set net-vm as a default gw + "option:dns-server,192.168.100.1" + ]; + } + ]; + hostConfiguration = lib.nixosSystem { + inherit system; + modules = [ nixos-generators.nixosModules.raw-efi self.nixosModules.common self.nixosModules.desktop self.nixosModules.host self.nixosModules.microvm + self.nixosModules.hw-x86_64-generic + self.nixosModules.reference-programs { ghaf = { hardware.x86_64.common.enable = true; - hardware.ax88179_178a.enable = true; - virtualization.microvm-host.enable = true; - virtualization.microvm-host.networkSupport = true; - host.networking.enable = true; - virtualization.microvm.netvm = { - enable = true; - extraModules = netvmExtraModules; + virtualization = { + microvm-host = { + enable = true; + networkSupport = true; + }; + + microvm.netvm = { + enable = true; + extraModules = netvmExtraModules; + }; }; + host.networking.enable = true; + # Enable all the default UI applications profiles = { applications.enable = true; @@ -63,7 +75,7 @@ # Uncomment this line to use Labwc instead of Weston: #graphics.compositor = "labwc"; }; - windows-launcher.enable = true; + reference.programs.windows-launcher.enable = true; }; #TODO: how to handle the majority of laptops that need a little @@ -80,26 +92,27 @@ "vfio-pci.ids=8086:a0f0" ]; } - ] - ++ extraModules; + ] ++ extraModules; + }; + in + { + inherit hostConfiguration; + name = "${name}-${variant}"; + package = hostConfiguration.config.system.build.${hostConfiguration.config.formatAttr}; }; - in { - inherit hostConfiguration; - name = "${name}-${variant}"; - package = hostConfiguration.config.system.build.${hostConfiguration.config.formatAttr}; - }; - debugModules = [{ghaf.development.usb-serial.enable = true;}]; + debugModules = [ { ghaf.development.usb-serial.enable = true; } ]; targets = [ (generic-x86 "debug" debugModules) - (generic-x86 "release" []) + (generic-x86 "release" [ ]) ]; -in { +in +{ flake = { - nixosConfigurations = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.hostConfiguration) targets); + nixosConfigurations = builtins.listToAttrs ( + map (t: lib.nameValuePair t.name t.hostConfiguration) targets + ); packages = { - x86_64-linux = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); + x86_64-linux = builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); }; }; } diff --git a/targets/imx8mp-evk/flake-module.nix b/targets/imx8mp-evk/flake-module.nix new file mode 100644 index 000000000..4dc8fbd2c --- /dev/null +++ b/targets/imx8mp-evk/flake-module.nix @@ -0,0 +1,82 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# i.MX8M Plus Evaluation Kit +{ + self, + lib, + inputs, + ... +}: +let + inherit (inputs) nixos-hardware; + name = "nxp-imx8mp-evk"; + system = "aarch64-linux"; + nxp-imx8mp-evk = + variant: extraModules: + let + hostConfiguration = lib.nixosSystem { + inherit system; + specialArgs = { + inherit lib; + }; + modules = [ + nixos-hardware.nixosModules.nxp-imx8mp-evk + self.nixosModules.common + self.nixosModules.host + self.nixosModules.imx8 + self.nixosModules.reference-personalize + { + boot = { + kernelParams = lib.mkForce [ "root=/dev/mmcblk0p2" ]; + loader = { + grub.enable = false; + generic-extlinux-compatible.enable = true; + }; + }; + + # Disable all the default UI applications + ghaf = { + profiles = { + release.enable = variant == "release"; + debug.enable = variant == "debug"; + }; + development = { + debug.tools.enable = variant == "debug"; + ssh.daemon.enable = true; + }; + firewall.kernel-modules.enable = true; + reference.personalize.keys.enable = variant == "debug"; + }; + nixpkgs = { + buildPlatform.system = "x86_64-linux"; + overlays = [ self.overlays.cross-compilation ]; + }; + hardware.deviceTree.name = lib.mkForce "freescale/imx8mp-evk.dtb"; + disabledModules = [ "profiles/all-hardware.nix" ]; + } + ] ++ extraModules; + }; + in + { + inherit hostConfiguration; + name = "${name}-${variant}"; + package = hostConfiguration.config.system.build.sdImage; + }; + debugModules = [ ]; + releaseModules = [ ]; + targets = [ + (nxp-imx8mp-evk "debug" debugModules) + (nxp-imx8mp-evk "release" releaseModules) + ]; +in +{ + flake = { + nixosConfigurations = builtins.listToAttrs ( + map (t: lib.nameValuePair t.name t.hostConfiguration) targets + ); + packages = { + aarch64-linux = builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); + }; + }; +} diff --git a/targets/imx8qm-mek/flake-module.nix b/targets/imx8qm-mek/flake-module.nix deleted file mode 100644 index bf695400a..000000000 --- a/targets/imx8qm-mek/flake-module.nix +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# i.MX8QuadMax Multisensory Enablement Kit -{ - self, - lib, - inputs, - ... -}: let - inherit (inputs) microvm nixos-generators nixos-hardware; - name = "imx8qm-mek"; - system = "aarch64-linux"; - imx8qm-mek = variant: extraModules: let - hostConfiguration = lib.nixosSystem { - inherit system; - modules = - [ - microvm.nixosModules.host - nixos-generators.nixosModules.raw-efi - nixos-hardware.nixosModules.nxp-imx8qm-mek - self.nixosModules.common - self.nixosModules.desktop - self.nixosModules.host - self.nixosModules.microvm - - { - ghaf = { - virtualization.microvm-host.enable = true; - host.networking.enable = true; - # TODO: NetVM enabled, but it does not include anything specific - # for iMX8 - virtualization.microvm.netvm.enable = true; - - # Enable all the default UI applications - profiles = { - applications.enable = true; - #TODO clean this up when the microvm is updated to latest - release.enable = variant == "release"; - debug.enable = variant == "debug"; - }; - }; - } - ] - ++ extraModules; - }; - in { - inherit hostConfiguration; - name = "${name}-${variant}"; - package = hostConfiguration.config.system.build.${hostConfiguration.config.formatAttr}; - }; - debugModules = []; - targets = [ - (imx8qm-mek "debug" debugModules) - (imx8qm-mek "release" []) - ]; -in { - flake = { - nixosConfigurations = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.hostConfiguration) targets); - packages = { - aarch64-linux = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); - }; - }; -} diff --git a/targets/laptop-hw-scan/flake-module.nix b/targets/laptop-hw-scan/flake-module.nix new file mode 100644 index 000000000..d703207ec --- /dev/null +++ b/targets/laptop-hw-scan/flake-module.nix @@ -0,0 +1,49 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# Laptop image to run hardware scan and generate config files +{ lib, self, ... }: +let + name = "laptop-hw-scan"; + system = "x86_64-linux"; + hw-scan = + let + hostConfiguration = lib.nixosSystem { + inherit system; + modules = [ + ( + { modulesPath, ... }: + { + imports = [ "${toString modulesPath}/installer/cd-dvd/installation-cd-minimal.nix" ]; + users.users.nixos.openssh.authorizedKeys.keys = + (import ../../modules/reference/personalize/authorizedSshKeys.nix).authorizedSshKeys; + systemd.services.wpa_supplicant.wantedBy = lib.mkForce [ "multi-user.target" ]; + systemd.services.sshd.wantedBy = lib.mkForce [ "multi-user.target" ]; + isoImage.isoBaseName = "ghaf"; + isoImage.squashfsCompression = "zstd -Xcompression-level 3"; + environment.systemPackages = [ self.packages.x86_64-linux.hardware-scan ]; + boot.kernelParams = [ + # TODO AMD support + "intel_iommu=on,sm_on" + "iommu=pt" + ]; + } + ) + ]; + }; + in + { + inherit hostConfiguration; + inherit name; + package = hostConfiguration.config.system.build.isoImage; + }; + targets = [ hw-scan ]; +in +{ + flake = { + nixosConfigurations = builtins.listToAttrs ( + map (t: lib.nameValuePair t.name t.hostConfiguration) targets + ); + packages.${system} = builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); + }; +} diff --git a/targets/laptop/flake-module.nix b/targets/laptop/flake-module.nix new file mode 100644 index 000000000..2b022cf91 --- /dev/null +++ b/targets/laptop/flake-module.nix @@ -0,0 +1,101 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# Configuration for laptop devices based on the hardware and usecase profile +{ + lib, + self, + inputs, + ... +}: +let + system = "x86_64-linux"; + + laptop-configuration = import ./laptop-configuration-builder.nix { inherit lib self inputs; }; + + targets = [ + # Laptop Debug configurations + (laptop-configuration "lenovo-x1-carbon-gen10" "debug" [ + self.nixosModules.disko-ab-partitions-v1 + { + ghaf = { + hardware.definition.configFile = "/lenovo-x1/definitions/x1-gen10.nix"; + reference.profiles.mvp-user-trial.enable = true; + }; + } + ]) + (laptop-configuration "lenovo-x1-carbon-gen11" "debug" [ + self.nixosModules.disko-ab-partitions-v1 + { + ghaf = { + hardware.definition.configFile = "/lenovo-x1/definitions/x1-gen11.nix"; + reference.profiles.mvp-user-trial.enable = true; + }; + } + ]) + (laptop-configuration "dell-latitude-7230" "debug" [ + self.nixosModules.disko-basic-partition-v1 + { + ghaf = { + hardware.definition.configFile = "/definitions/dell-latitude/dell-latitude-7230.nix"; + reference.profiles.mvp-user-trial.enable = true; + }; + } + ]) + (laptop-configuration "dell-latitude-7330" "debug" [ + self.nixosModules.disko-basic-partition-v1 + { + ghaf = { + hardware.definition.configFile = "/definitions/dell-latitude/dell-latitude-7330.nix"; + reference.profiles.mvp-user-trial.enable = true; + }; + } + ]) + + # Laptop Release configurations + (laptop-configuration "lenovo-x1-carbon-gen10" "release" [ + self.nixosModules.disko-ab-partitions-v1 + { + ghaf = { + hardware.definition.configFile = "/lenovo-x1/definitions/x1-gen10.nix"; + reference.profiles.mvp-user-trial.enable = true; + }; + } + ]) + (laptop-configuration "lenovo-x1-carbon-gen11" "release" [ + self.nixosModules.disko-ab-partitions-v1 + { + ghaf = { + hardware.definition.configFile = "/lenovo-x1/definitions/x1-gen11.nix"; + reference.profiles.mvp-user-trial.enable = true; + }; + } + ]) + (laptop-configuration "dell-latitude-7230" "release" [ + self.nixosModules.disko-basic-partition-v1 + { + ghaf = { + hardware.definition.configFile = "/definitions/dell-latitude/dell-latitude-7230.nix"; + reference.profiles.mvp-user-trial.enable = true; + }; + } + ]) + (laptop-configuration "dell-latitude-7330" "release" [ + self.nixosModules.disko-basic-partition-v1 + { + ghaf = { + hardware.definition.configFile = "/definitions/dell-latitude/dell-latitude-7330.nix"; + reference.profiles.mvp-user-trial.enable = true; + }; + } + ]) + ]; +in +{ + flake = { + nixosConfigurations = builtins.listToAttrs ( + map (t: lib.nameValuePair t.name t.hostConfiguration) targets + ); + packages.${system} = builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); + }; +} diff --git a/targets/laptop/laptop-configuration-builder.nix b/targets/laptop/laptop-configuration-builder.nix new file mode 100644 index 000000000..e47f04ffb --- /dev/null +++ b/targets/laptop/laptop-configuration-builder.nix @@ -0,0 +1,50 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + self, + inputs, + ... +}: +let + system = "x86_64-linux"; + + #TODO move this to a standalone function + #should it live in the library or just as a function file + mkLaptopConfiguration = + machineType: variant: extraModules: + let + hostConfiguration = lib.nixosSystem { + inherit system; + modules = [ + self.nixosModules.reference-profiles + self.nixosModules.laptop + inputs.lanzaboote.nixosModules.lanzaboote + + #TODO can we move microvm to the profile/laptop-x86? + self.nixosModules.microvm + #TODO see the twisted dependencies in common/desktop + + (_: { + time.timeZone = "Asia/Dubai"; + + ghaf = { + profiles = { + # variant type, turn on debug or release + debug.enable = variant == "debug"; + release.enable = variant == "release"; + # Enable below option for host hardening features + host-hardening.enable = false; + }; + }; + }) + ] ++ extraModules; + }; + in + { + inherit hostConfiguration; + name = "${machineType}-${variant}"; + package = hostConfiguration.config.system.build.diskoImages; + }; +in +mkLaptopConfiguration diff --git a/targets/lenovo-x1-installer/flake-module.nix b/targets/lenovo-x1-installer/flake-module.nix index c618b943d..27cd39d5f 100644 --- a/targets/lenovo-x1-installer/flake-module.nix +++ b/targets/lenovo-x1-installer/flake-module.nix @@ -2,69 +2,82 @@ # SPDX-License-Identifier: Apache-2.0 # # Lenovo X1 Carbon Installer -{ - lib, - self, - ... -}: let +{ lib, self, ... }: +let name = "lenovo-x1-carbon"; system = "x86_64-linux"; - installer = generation: variant: let - imagePath = self.packages.x86_64-linux."${name}-${generation}-${variant}" + "/disk1.raw"; - hostConfiguration = lib.nixosSystem { - inherit system; - modules = [ - ({ - pkgs, - modulesPath, - ... - }: let - installScript = pkgs.callPackage ../../packages/installer { - inherit imagePath; - }; - in { - imports = [ - "${toString modulesPath}/installer/cd-dvd/installation-cd-minimal.nix" - ../../modules/common/hardware/ax88179_178a.nix - ]; + installer = + generation: variant: + let + imagePath = self.packages.x86_64-linux."${name}-${generation}-${variant}" + "/disk1.raw.zst"; + hostConfiguration = lib.nixosSystem { + inherit system; + modules = [ + ( + { pkgs, modulesPath, ... }: + let + installScript = pkgs.callPackage ../../packages/installer { }; + in + { + imports = [ "${toString modulesPath}/installer/cd-dvd/installation-cd-minimal.nix" ]; + + environment.sessionVariables = { + IMG_PATH = imagePath; + }; + + # SSH key to installer for test automation. + users.users.nixos.openssh.authorizedKeys.keys = lib.mkIf ( + variant == "debug" + ) (import ../../modules/reference/personalize/authorizedSshKeys.nix).authorizedSshKeys; - ghaf.hardware.ax88179_178a.enable = true; + systemd.services.wpa_supplicant.wantedBy = lib.mkForce [ "multi-user.target" ]; + systemd.services.sshd.wantedBy = lib.mkForce [ "multi-user.target" ]; - # SSH key to installer for test automation. - users.users.nixos.openssh.authorizedKeys.keys = lib.mkIf (variant == "debug") (import ../../modules/common/development/authorized_ssh_keys.nix).authorizedKeys; + isoImage.isoBaseName = "ghaf"; + networking.hostName = "ghaf-installer"; - systemd.services.wpa_supplicant.wantedBy = lib.mkForce ["multi-user.target"]; - systemd.services.sshd.wantedBy = lib.mkForce ["multi-user.target"]; + environment.systemPackages = [ + installScript + self.packages.x86_64-linux.hardware-scan + ]; - isoImage.isoBaseName = "ghaf"; + services.getty = { + greetingLine = ''<<< Welcome to the Ghaf installer >>>''; + helpLine = lib.mkAfter '' - environment.systemPackages = [ - installScript - ]; + To run the installer, type + `sudo ghaf-installer` and select the installation target. + ''; + }; - # NOTE: Stop nixos complains about "warning: - # mdadm: Neither MAILADDR nor PROGRAM has been set. This will cause the `mdmon` service to crash." - # https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/installation-device.nix#L112 - boot.swraid.mdadmConf = "PROGRAM ${pkgs.coreutils}/bin/true"; - }) - ]; + isoImage.squashfsCompression = "zstd -Xcompression-level 3"; + + # NOTE: Stop nixos complains about "warning: + # mdadm: Neither MAILADDR nor PROGRAM has been set. This will cause the `mdmon` service to crash." + # https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/installation-device.nix#L112 + boot.swraid.mdadmConf = "PROGRAM ${pkgs.coreutils}/bin/true"; + } + ) + ]; + }; + in + { + inherit hostConfiguration; + name = "${name}-${generation}-${variant}-installer"; + package = hostConfiguration.config.system.build.isoImage; }; - in { - inherit hostConfiguration; - name = "${name}-${generation}-${variant}-installer"; - package = hostConfiguration.config.system.build.isoImage; - }; targets = [ (installer "gen10" "debug") (installer "gen11" "debug") (installer "gen10" "release") (installer "gen11" "release") ]; -in { +in +{ flake = { - nixosConfigurations = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.hostConfiguration) targets); - packages.${system} = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); + nixosConfigurations = builtins.listToAttrs ( + map (t: lib.nameValuePair t.name t.hostConfiguration) targets + ); + packages.${system} = builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); }; } diff --git a/targets/lenovo-x1/appvms/chromium.nix b/targets/lenovo-x1/appvms/chromium.nix deleted file mode 100644 index 8caaf9319..000000000 --- a/targets/lenovo-x1/appvms/chromium.nix +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright 2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -{pkgs, ...}: let - xdgPdfPort = 1200; -in { - name = "chromium"; - packages = let - # PDF XDG handler is executed when the user opens a PDF file in the browser - # The xdgopenpdf script sends a command to the guivm with the file path over TCP connection - xdgPdfItem = pkgs.makeDesktopItem { - name = "ghaf-pdf"; - desktopName = "Ghaf PDF handler"; - exec = "${xdgOpenPdf}/bin/xdgopenpdf %u"; - mimeTypes = ["application/pdf"]; - }; - xdgOpenPdf = pkgs.writeShellScriptBin "xdgopenpdf" '' - filepath=$(realpath $1) - echo "Opening $filepath" | systemd-cat -p info - echo $filepath | ${pkgs.netcat}/bin/nc -N gui-vm.ghaf ${toString xdgPdfPort} - ''; - in [ - pkgs.chromium - pkgs.pamixer - pkgs.xdg-utils - xdgPdfItem - xdgOpenPdf - ]; - # TODO create a repository of mac addresses to avoid conflicts - macAddress = "02:00:00:03:05:01"; - ramMb = 3072; - cores = 4; - extraModules = [ - { - # Enable pulseaudio for user ghaf - sound.enable = true; - hardware.pulseaudio.enable = true; - users.extraUsers.ghaf.extraGroups = ["audio"]; - - time.timeZone = "Asia/Dubai"; - - microvm.qemu.extraArgs = [ - # Connect sound device to hosts pulseaudio socket - "-audiodev" - "pa,id=pa1,server=unix:/run/pulse/native" - # Add HDA sound device to guest - "-device" - "intel-hda" - "-device" - "hda-duplex,audiodev=pa1" - # Lenovo X1 integrated usb webcam - "-device" - "qemu-xhci" - "-device" - "usb-host,hostbus=3,hostport=8" - ]; - microvm.devices = []; - - # Disable chromium built-in PDF viewer to make it execute xdg-open - programs.chromium.enable = true; - programs.chromium.extraOpts."AlwaysOpenPdfExternally" = true; - # Set default PDF XDG handler - xdg.mime.defaultApplications."application/pdf" = "ghaf-pdf.desktop"; - } - ]; - borderColor = "#ff5733"; -} diff --git a/targets/lenovo-x1/appvms/default.nix b/targets/lenovo-x1/appvms/default.nix deleted file mode 100644 index d6b3ad303..000000000 --- a/targets/lenovo-x1/appvms/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -{pkgs, ...}: let - chromium = import ./chromium.nix {inherit pkgs;}; - gala = import ./gala.nix {inherit pkgs;}; - zathura = import ./zathura.nix {inherit pkgs;}; -in [ - chromium - gala - zathura -] diff --git a/targets/lenovo-x1/appvms/gala.nix b/targets/lenovo-x1/appvms/gala.nix deleted file mode 100644 index 20aff4be1..000000000 --- a/targets/lenovo-x1/appvms/gala.nix +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -{pkgs, ...}: { - name = "gala"; - packages = [pkgs.gala-app]; - macAddress = "02:00:00:03:06:01"; - ramMb = 1536; - cores = 2; - extraModules = [ - { - time.timeZone = "Asia/Dubai"; - } - ]; - borderColor = "#33ff57"; -} diff --git a/targets/lenovo-x1/appvms/zathura.nix b/targets/lenovo-x1/appvms/zathura.nix deleted file mode 100644 index d2ca18710..000000000 --- a/targets/lenovo-x1/appvms/zathura.nix +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -{pkgs, ...}: { - name = "zathura"; - packages = [pkgs.zathura]; - macAddress = "02:00:00:03:07:01"; - ramMb = 512; - cores = 1; - extraModules = [ - { - time.timeZone = "Asia/Dubai"; - } - ]; - borderColor = "#337aff"; -} diff --git a/targets/lenovo-x1/debugModules.nix b/targets/lenovo-x1/debugModules.nix deleted file mode 100644 index 189f91cdf..000000000 --- a/targets/lenovo-x1/debugModules.nix +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -[ - { - ghaf.development.usb-serial.enable = true; - ghaf.profiles.debug.enable = true; - ghaf.host.secureboot.enable = false; - ghaf.host.kernel.hardening.usb.enable = false; - ghaf.host.kernel.hardening.debug.enable = false; - } -] diff --git a/targets/lenovo-x1/everything.nix b/targets/lenovo-x1/everything.nix deleted file mode 100644 index fb24b326e..000000000 --- a/targets/lenovo-x1/everything.nix +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -{ - self, - lib, - microvm, - lanzaboote, - name, - system, - ... -}: let - # From here - # These can be added back to default.nix to form part of the target template - debugModules = import ./debugModules.nix; - releaseModules = import ./releaseModules.nix; - - ## To here - - lenovo-x1 = generation: variant: extraModules: let - hwDefinition = import ../../modules/common/hardware/lenovo-x1/definitions { - inherit generation lib; - }; - hostConfiguration = lib.nixosSystem { - inherit system; - modules = - [ - lanzaboote.nixosModules.lanzaboote - microvm.nixosModules.host - self.nixosModules.common - self.nixosModules.desktop - self.nixosModules.host - self.nixosModules.lanzaboote - self.nixosModules.microvm - - self.nixosModules.disko-lenovo-x1-basic-v1 - - ./sshkeys.nix - ({ - pkgs, - config, - ... - }: let - powerControl = pkgs.callPackage ../../packages/powercontrol {}; - in { - security.polkit.extraConfig = powerControl.polkitExtraConfig; - services.udev.extraRules = hwDefinition.udevRules; - time.timeZone = "Asia/Dubai"; - - # Enable pulseaudio support for host as a service - sound.enable = true; - hardware.pulseaudio.enable = true; - hardware.pulseaudio.systemWide = true; - # Add systemd to require pulseaudio before starting chromium-vm - systemd.services."microvm@chromium-vm".after = ["pulseaudio.service"]; - systemd.services."microvm@chromium-vm".requires = ["pulseaudio.service"]; - - # Allow microvm user to access pulseaudio - hardware.pulseaudio.extraConfig = "load-module module-combine-sink module-native-protocol-unix auth-anonymous=1"; - users.extraUsers.microvm.extraGroups = ["audio" "pulse-access"]; - - environment.etc.${config.ghaf.security.sshKeys.getAuthKeysFilePathInEtc} = import ./getAuthKeysSource.nix {inherit pkgs config;}; - services.openssh = config.ghaf.security.sshKeys.sshAuthorizedKeysCommand; - - disko.devices.disk = config.ghaf.hardware.definition.disks; - - ghaf = { - hardware.definition = hwDefinition; - # To enable guest hardening enable host hardening first - host.kernel.hardening.enable = false; - host.kernel.hardening.virtualization.enable = false; - host.kernel.hardening.networking.enable = false; - host.kernel.hardening.inputdevices.enable = false; - - guest.kernel.hardening.enable = false; - guest.kernel.hardening.graphics.enable = false; - - host.kernel.hardening.hypervisor.enable = false; - - hardware.x86_64.common.enable = true; - hardware.ax88179_178a.enable = true; - - security.tpm2.enable = true; - - virtualization.microvm-host.enable = true; - virtualization.microvm-host.networkSupport = true; - - host.networking.enable = true; - virtualization.microvm.netvm = { - enable = true; - extraModules = import ./netvmExtraModules.nix { - inherit lib pkgs microvm; - configH = config; - }; - }; - virtualization.microvm.guivm = { - enable = true; - extraModules = - # TODO convert this to an actual module - import ./guivmExtraModules.nix { - inherit lib pkgs microvm; - configH = config; - }; - }; - virtualization.microvm.appvm = { - enable = true; - vms = import ./appvms/default.nix {inherit pkgs;}; - }; - - # Enable all the default UI applications - profiles = { - applications.enable = false; - }; - windows-launcher = { - enable = true; - spice = true; - }; - }; - }) - - #TODO: how to handle the majority of laptops that need a little - # something extra? - # SEE: https://github.com/NixOS/nixos-hardware/blob/master/flake.nix - # nixos-hardware.nixosModules.lenovo-thinkpad-x1-10th-gen - - ({config, ...}: { - boot.kernelParams = let - filterDevices = builtins.filter (d: d.vendorId != null && d.productId != null); - mapPciIdsToString = builtins.map (d: "${d.vendorId}:${d.productId}"); - vfioPciIds = mapPciIdsToString (filterDevices ( - config.ghaf.hardware.definition.network.pciDevices - ++ config.ghaf.hardware.definition.gpu.pciDevices - )); - in [ - "intel_iommu=on,sm_on" - "iommu=pt" - # Prevent i915 module from being accidentally used by host - "module_blacklist=i915" - - "vfio-pci.ids=${builtins.concatStringsSep "," vfioPciIds}" - ]; - - boot.initrd.availableKernelModules = ["nvme"]; - }) - ] - ++ extraModules; - }; - in { - inherit hostConfiguration; - name = "${name}-${generation}-${variant}"; - package = hostConfiguration.config.system.build.diskoImages; - }; -in [ - (lenovo-x1 "gen10" "debug" debugModules) - (lenovo-x1 "gen11" "debug" debugModules) - (lenovo-x1 "gen10" "release" releaseModules) - (lenovo-x1 "gen11" "release" releaseModules) -] diff --git a/targets/lenovo-x1/flake-module.nix b/targets/lenovo-x1/flake-module.nix deleted file mode 100644 index 7eb158aee..000000000 --- a/targets/lenovo-x1/flake-module.nix +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -# Configuration for Lenovo X1 Carbon Gen 11 -{ - inputs, - lib, - self, - ... -}: let - inherit (inputs) microvm lanzaboote disko; - name = "lenovo-x1-carbon"; - system = "x86_64-linux"; - targets = import ./everything.nix {inherit self lib microvm lanzaboote disko name system;}; -in { - flake = { - nixosConfigurations = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.hostConfiguration) targets); - packages.${system} = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); - }; -} diff --git a/targets/lenovo-x1/getAuthKeysSource.nix b/targets/lenovo-x1/getAuthKeysSource.nix deleted file mode 100644 index 28cf0bb3a..000000000 --- a/targets/lenovo-x1/getAuthKeysSource.nix +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -{ - pkgs, - config, - ... -}: { - source = let - script = pkgs.writeShellScriptBin config.ghaf.security.sshKeys.getAuthKeysFileName '' - [[ "$1" != "ghaf" ]] && exit 0 - ${pkgs.coreutils}/bin/cat ${config.ghaf.security.sshKeys.waypipeSshPublicKeyFile} - ''; - in "${script}/bin/${config.ghaf.security.sshKeys.getAuthKeysFileName}"; - mode = "0555"; -} diff --git a/targets/lenovo-x1/guivmExtraModules.nix b/targets/lenovo-x1/guivmExtraModules.nix deleted file mode 100644 index f5f18fecc..000000000 --- a/targets/lenovo-x1/guivmExtraModules.nix +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -{ - lib, - pkgs, - microvm, - configH, - ... -}: let - openPdf = pkgs.callPackage ./openPdf.nix { - inherit pkgs; - inherit (configH.ghaf.security.sshKeys) sshKeyPath; - }; - # TODO generalize this TCP port used by PDF XDG handler - xdgPdfPort = 1200; - - winConfig = configH.ghaf.windows-launcher; - - guivmPCIPassthroughModule = { - microvm.devices = lib.mkForce ( - builtins.map (d: { - bus = "pci"; - inherit (d) path; - }) - configH.ghaf.hardware.definition.gpu.pciDevices - ); - }; - - guivmVirtioInputHostEvdevModule = { - microvm.qemu.extraArgs = - builtins.concatMap (d: [ - "-device" - "virtio-input-host-pci,evdev=${d}" - ]) - configH.ghaf.hardware.definition.virtioInputHostEvdevs; - }; - - guivmExtraConfigurations = { - ghaf.hardware.definition.network.pciDevices = configH.ghaf.hardware.definition.network.pciDevices; - ghaf.profiles.graphics.compositor = "labwc"; - ghaf.graphics.launchers = let - hostAddress = "192.168.101.2"; - powerControl = pkgs.callPackage ../../packages/powercontrol {}; - powerControlIcons = pkgs.gnome.callPackage ../../packages/powercontrol/png-icons.nix {}; - in [ - { - name = "chromium"; - path = "${pkgs.openssh}/bin/ssh -i ${configH.ghaf.security.sshKeys.sshKeyPath} -o StrictHostKeyChecking=no chromium-vm.ghaf run-waypipe chromium --enable-features=UseOzonePlatform --ozone-platform=wayland"; - icon = "${../../assets/icons/png/browser.png}"; - } - - { - name = "gala"; - path = "${pkgs.openssh}/bin/ssh -i ${configH.ghaf.security.sshKeys.sshKeyPath} -o StrictHostKeyChecking=no gala-vm.ghaf run-waypipe gala --enable-features=UseOzonePlatform --ozone-platform=wayland"; - icon = "${../../assets/icons/png/app.png}"; - } - - { - name = "zathura"; - path = "${pkgs.openssh}/bin/ssh -i ${configH.ghaf.security.sshKeys.sshKeyPath} -o StrictHostKeyChecking=no zathura-vm.ghaf run-waypipe zathura"; - icon = "${../../assets/icons/png/pdf.png}"; - } - - { - name = "windows"; - path = "${pkgs.virt-viewer}/bin/remote-viewer -f spice://${winConfig.spice-host}:${toString winConfig.spice-port}"; - icon = "${../../assets/icons/png/windows.png}"; - } - - { - name = "nm-launcher"; - path = "${pkgs.nm-launcher}/bin/nm-launcher"; - icon = "${pkgs.networkmanagerapplet}/share/icons/hicolor/22x22/apps/nm-device-wwan.png"; - } - - { - name = "poweroff"; - path = "${powerControl.makePowerOffCommand { - inherit hostAddress; - inherit (configH.ghaf.security.sshKeys) sshKeyPath; - }}"; - icon = "${powerControlIcons}/${powerControlIcons.relativeShutdownIconPath}"; - } - - { - name = "reboot"; - path = "${powerControl.makeRebootCommand { - inherit hostAddress; - inherit (configH.ghaf.security.sshKeys) sshKeyPath; - }}"; - icon = "${powerControlIcons}/${powerControlIcons.relativeRebootIconPath}"; - } - - # Temporarly disabled as it doesn't work stable - # { - # path = powerControl.makeSuspendCommand {inherit hostAddress sshKeyPath;}; - # icon = "${adwaitaIconsRoot}/media-playback-pause-symbolic.symbolic.png"; - # } - - # Temporarly disabled as it doesn't work at all - # { - # path = powerControl.makeHibernateCommand {inherit hostAddress sshKeyPath;}; - # icon = "${adwaitaIconsRoot}/media-record-symbolic.symbolic.png"; - # } - ]; - - time.timeZone = "Asia/Dubai"; - - # PDF XDG handler service receives a PDF file path from the chromium-vm and executes the openpdf script - systemd.user = { - sockets."pdf" = { - unitConfig = { - Description = "PDF socket"; - }; - socketConfig = { - ListenStream = "${toString xdgPdfPort}"; - Accept = "yes"; - }; - wantedBy = ["sockets.target"]; - }; - - services."pdf@" = { - description = "PDF opener"; - serviceConfig = { - ExecStart = "${openPdf}/bin/openPdf"; - StandardInput = "socket"; - StandardOutput = "journal"; - StandardError = "journal"; - }; - }; - }; - - # Open TCP port for the PDF XDG socket - networking.firewall.allowedTCPPorts = [xdgPdfPort]; - # Early KMS needed for GNOME to work inside GuiVM - boot.initrd.kernelModules = ["i915"]; - - microvm.qemu = { - extraArgs = [ - # Lenovo X1 Lid button - "-device" - "button" - # Lenovo X1 battery - "-device" - "battery" - # Lenovo X1 AC adapter - "-device" - "acad" - # Connect sound device to hosts pulseaudio socket - "-audiodev" - "pa,id=pa1,server=unix:/run/pulse/native" - ]; - }; - }; -in [ - ./sshkeys.nix - guivmPCIPassthroughModule - guivmVirtioInputHostEvdevModule - guivmExtraConfigurations -] diff --git a/targets/lenovo-x1/netvmExtraModules.nix b/targets/lenovo-x1/netvmExtraModules.nix deleted file mode 100644 index f54933729..000000000 --- a/targets/lenovo-x1/netvmExtraModules.nix +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -{ - lib, - pkgs, - microvm, - configH, - ... -}: let - netvmPCIPassthroughModule = { - microvm.devices = lib.mkForce ( - builtins.map (d: { - bus = "pci"; - inherit (d) path; - }) - configH.ghaf.hardware.definition.network.pciDevices - ); - }; - - netvmAdditionalConfig = { - # Add the waypipe-ssh public key to the microvm - microvm = { - shares = [ - { - tag = configH.ghaf.security.sshKeys.waypipeSshPublicKeyName; - source = configH.ghaf.security.sshKeys.waypipeSshPublicKeyDir; - mountPoint = configH.ghaf.security.sshKeys.waypipeSshPublicKeyDir; - } - ]; - }; - fileSystems.${configH.ghaf.security.sshKeys.waypipeSshPublicKeyDir}.options = ["ro"]; - - # For WLAN firmwares - hardware.enableRedistributableFirmware = true; - - networking = { - # wireless is disabled because we use NetworkManager for wireless - wireless.enable = lib.mkForce false; - networkmanager = { - enable = true; - unmanaged = ["ethint0"]; - }; - }; - # noXlibs=false; needed for NetworkManager stuff - environment.noXlibs = false; - environment.etc."NetworkManager/system-connections/Wifi-1.nmconnection" = { - text = '' - [connection] - id=Wifi-1 - uuid=33679db6-4cde-11ee-be56-0242ac120002 - type=wifi - [wifi] - mode=infrastructure - ssid=SSID_OF_NETWORK - [wifi-security] - key-mgmt=wpa-psk - psk=WPA_PASSWORD - [ipv4] - method=auto - [ipv6] - method=disabled - [proxy] - ''; - mode = "0600"; - }; - # Waypipe-ssh key is used here to create keys for ssh tunneling to forward D-Bus sockets. - # SSH is very picky about to file permissions and ownership and will - # accept neither direct path inside /nix/store or symlink that points - # there. Therefore we copy the file to /etc/ssh/get-auth-keys (by - # setting mode), instead of symlinking it. - environment.etc.${configH.ghaf.security.sshKeys.getAuthKeysFilePathInEtc} = import ./getAuthKeysSource.nix { - inherit pkgs; - config = configH; - }; - # Add simple wi-fi connection helper - environment.systemPackages = lib.mkIf configH.ghaf.profiles.debug.enable [pkgs.wifi-connector-nmcli]; - - services.openssh = configH.ghaf.security.sshKeys.sshAuthorizedKeysCommand; - - time.timeZone = "Asia/Dubai"; - }; -in [./sshkeys.nix netvmPCIPassthroughModule netvmAdditionalConfig] diff --git a/targets/microchip-icicle-kit/flake-module.nix b/targets/microchip-icicle-kit/flake-module.nix index 7da8c0fa5..ba5ee7729 100644 --- a/targets/microchip-icicle-kit/flake-module.nix +++ b/targets/microchip-icicle-kit/flake-module.nix @@ -7,19 +7,22 @@ lib, self, ... -}: let - inherit (inputs) nixos-hardware nixpkgs; +}: +let + inherit (inputs) nixos-hardware; name = "microchip-icicle-kit"; system = "riscv64-linux"; - microchip-icicle-kit = variant: extraModules: let - hostConfiguration = lib.nixosSystem { - inherit system; - modules = - [ + microchip-icicle-kit = + variant: extraModules: + let + hostConfiguration = lib.nixosSystem { + inherit system; + modules = [ nixos-hardware.nixosModules.microchip-icicle-kit self.nixosModules.common self.nixosModules.host self.nixosModules.polarfire + self.nixosModules.reference-personalize { boot = { @@ -40,37 +43,40 @@ ssh.daemon.enable = true; }; firewall.kernel-modules.enable = true; + reference.personalize.keys.enable = variant == "debug"; }; nixpkgs = { buildPlatform.system = "x86_64-linux"; hostPlatform.system = "riscv64-linux"; - overlays = [ - self.overlays.cross-compilation - ]; + overlays = [ self.overlays.cross-compilation ]; }; - boot.kernelParams = ["root=/dev/mmcblk0p2" "rootdelay=5"]; - disabledModules = ["profiles/all-hardware.nix"]; + boot.kernelParams = [ + "root=/dev/mmcblk0p2" + "rootdelay=5" + ]; + disabledModules = [ "profiles/all-hardware.nix" ]; } - ] - ++ extraModules; + ] ++ extraModules; + }; + in + { + inherit hostConfiguration; + name = "${name}-${variant}-from-x86_64"; + package = hostConfiguration.config.system.build.sdImage; }; - in { - inherit hostConfiguration; - name = "${name}-${variant}"; - package = hostConfiguration.config.system.build.sdImage; - }; targets = [ - (microchip-icicle-kit "debug" []) - (microchip-icicle-kit "release" []) + (microchip-icicle-kit "debug" [ ]) + (microchip-icicle-kit "release" [ ]) ]; -in { +in +{ flake = { - nixosConfigurations = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.hostConfiguration) targets); + nixosConfigurations = builtins.listToAttrs ( + map (t: lib.nameValuePair t.name t.hostConfiguration) targets + ); packages = { - riscv64-linux = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); + x86_64-linux = builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); }; }; } diff --git a/targets/nvidia-jetson-orin/cross-compilation.nix b/targets/nvidia-jetson-orin/cross-compilation.nix index 8f16d935b..181bcfb3d 100644 --- a/targets/nvidia-jetson-orin/cross-compilation.nix +++ b/targets/nvidia-jetson-orin/cross-compilation.nix @@ -6,8 +6,6 @@ { nixpkgs = { buildPlatform.system = "x86_64-linux"; - overlays = [ - (import ../../overlays/cross-compilation) - ]; + overlays = [ (import ../../overlays/cross-compilation) ]; }; } diff --git a/targets/nvidia-jetson-orin/flake-module.nix b/targets/nvidia-jetson-orin/flake-module.nix index a28022de7..fd7cf21e7 100644 --- a/targets/nvidia-jetson-orin/flake-module.nix +++ b/targets/nvidia-jetson-orin/flake-module.nix @@ -8,52 +8,61 @@ lib, self, ... -}: let - inherit (inputs) nixpkgs nixos-generators microvm jetpack-nixos; +}: +let + inherit (inputs) nixpkgs nixos-generators jetpack-nixos; name = "nvidia-jetson-orin"; system = "aarch64-linux"; - nvidia-jetson-orin = som: variant: extraModules: let - netvmExtraModules = [ - { - # The Nvidia Orin hardware dependent configuration is in - # modules/jetpack and modules/jetpack-microvm. Please refer to that - # section for hardware dependent netvm configuration. + nvidia-jetson-orin = + som: variant: extraModules: + let + netvmExtraModules = [ + { + # The Nvidia Orin hardware dependent configuration is in + # modules/jetpack and modules/jetpack-microvm. Please refer to that + # section for hardware dependent netvm configuration. - # Wireless Configuration. Orin AGX has WiFi enabled where Orin NX does - # not. + # Wireless Configuration. Orin AGX has WiFi enabled where Orin NX does + # not. - # To enable or disable wireless - networking.wireless.enable = som == "agx"; + # To enable or disable wireless + networking.wireless.enable = som == "agx"; - # For WLAN firmwares - hardware = { - enableRedistributableFirmware = som == "agx"; - wirelessRegulatoryDatabase = true; - }; - } - ]; - gpiovmExtraModules = [ - { - # The Nvidia Orin hardware dependent configuration is in - # modules/jetpack and modules/jetpack-microvm. Please refer to that - # section for hardware dependent gpiovm configuration. - } - ]; - hostConfiguration = lib.nixosSystem { - inherit system; + # For WLAN firmwares + hardware = { + enableRedistributableFirmware = som == "agx"; + wirelessRegulatoryDatabase = true; + }; - modules = - [ + services.dnsmasq.settings.dhcp-option = [ + "option:router,192.168.100.1" # set net-vm as a default gw + "option:dns-server,192.168.100.1" + ]; + } + ]; + gpiovmExtraModules = [ + { + # The Nvidia Orin hardware dependent configuration is in + # modules/jetpack and modules/jetpack-microvm. Please refer to that + # section for hardware dependent gpiovm configuration. + } + ]; + hostConfiguration = lib.nixosSystem { + inherit system; + + modules = [ (nixos-generators + "/format-module.nix") ../../modules/jetpack/nvidia-jetson-orin/format-module.nix jetpack-nixos.nixosModules.default - microvm.nixosModules.host self.nixosModules.common self.nixosModules.desktop self.nixosModules.host self.nixosModules.jetpack self.nixosModules.jetpack-microvm self.nixosModules.microvm + self.nixosModules.reference-programs + self.nixosModules.reference-personalize + { ghaf = { hardware.nvidia.orin = { @@ -67,69 +76,75 @@ hardware.nvidia = { virtualization.enable = true; virtualization.host.bpmp.enable = false; + virtualization.host.gpio.enable = som == "agx"; passthroughs.host.uarta.enable = false; # passthroughs.uarti_net_vm.enable = som == "agx"; passthroughs.uarti_net_vm.enable = false; - virtualization.host.gpio.enable = som == "agx"; + # passthroughs.gpio_vm.enable = false; # not implemented }; - virtualization.microvm-host.enable = true; - virtualization.microvm-host.networkSupport = true; - # virtualization.microvm-host.networkSupport = false; - host.networking.enable = true; - - virtualization.microvm.netvm.enable = true; - # virtualization.microvm.netvm.enable = false; - virtualization.microvm.netvm.extraModules = netvmExtraModules; + virtualization = { + microvm-host = { + enable = true; + networkSupport = true; + }; - virtualization.microvm.gpiovm.enable = true; - virtualization.microvm.gpiovm.extraModules = gpiovmExtraModules; + microvm = { + netvm = { + enable = true; + extraModules = netvmExtraModules; + }; + gpiovm = { + enable = true; + extraModules = gpiovmExtraModules; + }; + }; + }; # Enable all the default UI applications profiles = { applications.enable = true; release.enable = variant == "release"; debug.enable = variant == "debug"; + graphics.renderer = "gles2"; }; - windows-launcher.enable = true; + reference.programs.windows-launcher.enable = true; + reference.personalize.keys.enable = variant == "debug"; + + # To enable screen locking set to true + graphics.labwc.autolock.enable = false; }; } - (import ./optee.nix {inherit jetpack-nixos;}) - ] - ++ extraModules; + (import ./optee.nix { }) + ] ++ extraModules; + }; + in + { + inherit hostConfiguration; + name = "${name}-${som}-${variant}"; + package = hostConfiguration.config.system.build.${hostConfiguration.config.formatAttr}; }; - in { - inherit hostConfiguration; - name = "${name}-${som}-${variant}"; - package = hostConfiguration.config.system.build.${hostConfiguration.config.formatAttr}; - }; - nvidia-jetson-orin-agx-debug = nvidia-jetson-orin "agx" "debug" []; - nvidia-jetson-orin-agx-release = nvidia-jetson-orin "agx" "release" []; - nvidia-jetson-orin-nx-debug = nvidia-jetson-orin "nx" "debug" []; - nvidia-jetson-orin-nx-release = nvidia-jetson-orin "nx" "release" []; - generate-nodemoapps = tgt: + nvidia-jetson-orin-agx-debug = nvidia-jetson-orin "agx" "debug" [ ]; + nvidia-jetson-orin-agx-release = nvidia-jetson-orin "agx" "release" [ ]; + nvidia-jetson-orin-nx-debug = nvidia-jetson-orin "nx" "debug" [ ]; + nvidia-jetson-orin-nx-release = nvidia-jetson-orin "nx" "release" [ ]; + generate-nodemoapps = + tgt: tgt // rec { name = tgt.name + "-nodemoapps"; hostConfiguration = tgt.hostConfiguration.extendModules { - modules = [ - { - ghaf.graphics.enableDemoApplications = lib.mkForce false; - } - ]; + modules = [ { ghaf.graphics.enableDemoApplications = lib.mkForce false; } ]; }; package = hostConfiguration.config.system.build.${hostConfiguration.config.formatAttr}; }; - generate-cross-from-x86_64 = tgt: + generate-cross-from-x86_64 = + tgt: tgt // rec { name = tgt.name + "-from-x86_64"; - hostConfiguration = tgt.hostConfiguration.extendModules { - modules = [ - ./cross-compilation.nix - ]; - }; + hostConfiguration = tgt.hostConfiguration.extendModules { modules = [ ./cross-compilation.nix ]; }; package = hostConfiguration.config.system.build.${hostConfiguration.config.formatAttr}; }; # Base targets to use for generating demoapps and cross-compilation targets @@ -144,7 +159,8 @@ crossTargets = map generate-cross-from-x86_64 targets; mkFlashScript = import ../../lib/mk-flash-script; # Generate flash script variant which flashes both QSPI and eMMC - generate-flash-script = tgt: flash-tools-system: + generate-flash-script = + tgt: flash-tools-system: mkFlashScript { inherit nixpkgs; inherit (tgt) hostConfiguration; @@ -153,35 +169,48 @@ }; # Generate flash script variant which flashes QSPI only. Useful for Orin NX # and non-eMMC based development. - generate-flash-qspi = tgt: flash-tools-system: + generate-flash-qspi = + tgt: flash-tools-system: mkFlashScript { inherit nixpkgs; hostConfiguration = tgt.hostConfiguration.extendModules { - modules = [ - { - ghaf.hardware.nvidia.orin.flashScriptOverrides.onlyQSPI = true; - } - ]; + modules = [ { ghaf.hardware.nvidia.orin.flashScriptOverrides.onlyQSPI = true; } ]; }; inherit jetpack-nixos; inherit flash-tools-system; }; -in { +in +{ flake = { - nixosConfigurations = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.hostConfiguration) (targets ++ crossTargets)); + nixosConfigurations = builtins.listToAttrs ( + map (t: lib.nameValuePair t.name t.hostConfiguration) (targets ++ crossTargets) + ); packages = { aarch64-linux = builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets) # EXPERIMENTAL: The aarch64-linux hosted flashing support is experimental # and it simply might not work. Providing the script anyway - // builtins.listToAttrs (map (t: lib.nameValuePair "${t.name}-flash-script" (generate-flash-script t "aarch64-linux")) targets) - // builtins.listToAttrs (map (t: lib.nameValuePair "${t.name}-flash-qspi" (generate-flash-qspi t "aarch64-linux")) targets); + // builtins.listToAttrs ( + map ( + t: lib.nameValuePair "${t.name}-flash-script" (generate-flash-script t "aarch64-linux") + ) targets + ) + // builtins.listToAttrs ( + map (t: lib.nameValuePair "${t.name}-flash-qspi" (generate-flash-qspi t "aarch64-linux")) targets + ); x86_64-linux = builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) crossTargets) - // builtins.listToAttrs (map (t: lib.nameValuePair "${t.name}-flash-script" (generate-flash-script t "x86_64-linux")) (targets ++ crossTargets)) - // builtins.listToAttrs (map (t: lib.nameValuePair "${t.name}-flash-qspi" (generate-flash-qspi t "x86_64-linux")) (targets ++ crossTargets)); + // builtins.listToAttrs ( + map (t: lib.nameValuePair "${t.name}-flash-script" (generate-flash-script t "x86_64-linux")) ( + targets ++ crossTargets + ) + ) + // builtins.listToAttrs ( + map (t: lib.nameValuePair "${t.name}-flash-qspi" (generate-flash-qspi t "x86_64-linux")) ( + targets ++ crossTargets + ) + ); }; }; } diff --git a/targets/nvidia-jetson-orin/optee.nix b/targets/nvidia-jetson-orin/optee.nix index b12903761..98d27f84b 100644 --- a/targets/nvidia-jetson-orin/optee.nix +++ b/targets/nvidia-jetson-orin/optee.nix @@ -1,13 +1,15 @@ # SPDX-FileCopyrightText: 2022-2023 TII (SSRC) and the Ghaf contributors # # SPDX-License-Identifier: Apache-2.0 -{jetpack-nixos}: ( +_: +( { pkgs, config, lib, ... - }: let + }: + let # TODO: Refactor this later, if this gets proper implementation on the # jetpack-nixos stdenv = pkgs.gcc9Stdenv; @@ -16,15 +18,15 @@ opteeSource = pkgs.fetchgit { url = "https://nv-tegra.nvidia.com/r/tegra/optee-src/nv-optee"; - rev = "jetson_${l4tVersion}"; - sha256 = "sha256-44RBXFNUlqZoq3OY/OFwhiU4Qxi4xQNmetFmlrr6jzY="; + rev = builtins.trace "jetson_${l4tVersion}" "jetson_${l4tVersion}"; + sha256 = "sha256-jJOMig2+9FlKA9gJUCH/dva7ZtAq1typZSNGKyM7tlg="; }; opteeXtest = stdenv.mkDerivation { pname = "optee_xtest"; version = l4tVersion; src = opteeSource; - nativeBuildInputs = [(pkgs.buildPackages.python3.withPackages (p: [p.cryptography]))]; + nativeBuildInputs = [ (pkgs.buildPackages.python3.withPackages (p: [ p.cryptography ])) ]; postPatch = '' patchShebangs --build $(find optee/optee_test -type d -name scripts -printf '%p ') ''; @@ -47,7 +49,7 @@ pname = "pkcs11"; version = l4tVersion; src = opteeSource; - nativeBuildInputs = [(pkgs.buildPackages.python3.withPackages (p: [p.cryptography]))]; + nativeBuildInputs = [ (pkgs.buildPackages.python3.withPackages (p: [ p.cryptography ])) ]; makeFlags = [ "-C optee/optee_os/ta/pkcs11" "CROSS_COMPILE=${stdenv.cc.targetPrefix}" @@ -68,67 +70,74 @@ pkcs11-tool-optee = pkgs.writeShellScriptBin "pkcs11-tool-optee" '' exec "${pkgs.opensc}/bin/pkcs11-tool" --module "${opteeClient}/lib/libckteec.so" $@ ''; - in { - hardware.nvidia-jetpack.firmware.optee.supplicant.trustedApplications = let - xTestTaDir = "${opteeXtest}/ta"; - xTestTaPaths = - builtins.map (ta: { - name = ta; - path = xTestTaDir + "/" + ta; - }) [ - # List of OP-TEE's xtest required TA's - # - # A short guide about a ways of constructing xtest TA list - # - # A) Run xtest and based on errors add TAs to the list - # - Run xtest and you might see following error - # E/LD: init_elf:453 sys_open_ta_bin(cb3e5ba0-adf1-11e0-998b-0002a5d5c51b) - # E/TC:?? 0 ldelf_init_with_ldelf:131 ldelf failed with res: 0xffff0008 - # --> Add cb3e5ba0-adf1-11e0-998b-0002a5d5c51b.ta into list and repeat - # - # B) From OP-TEE's xtest sources https://github.com/OP-TEE/optee_test - # - Navigate into optee_test repo and run - # $ find ta -path ta/supp_plugin -prune -o -name Makefile -exec grep -oP 'BINARY = \K.*' {} \; - # --> Above comaand produces a list of TAs UUID - # --> It does not produce all UUID due some of them are hardcode into source files - # --> It produce more TA than needed - # - # C) At "find ./out -name "*.ta"" into opteeXtest derivation installPhase - # and uild package with "-L"-flag - # --> Scroll output until find TAs - # ./out/ta/crypt/cb3e5ba0-adf1-11e0-998b-0002a5d5c51b.ta - # ./out/ta/concurrent_large/5ce0c432-0ab0-40e5-a056-782ca0e6aba2.ta - # - # Below list used option C + in + { + hardware.nvidia-jetpack.firmware.optee.supplicant.trustedApplications = + let + xTestTaDir = "${opteeXtest}/ta"; + xTestTaPaths = + builtins.map + (ta: { + name = ta; + path = xTestTaDir + "/" + ta; + }) + [ + # List of OP-TEE's xtest required TA's + # + # A short guide about a ways of constructing xtest TA list + # + # A) Run xtest and based on errors add TAs to the list + # - Run xtest and you might see following error + # E/LD: init_elf:453 sys_open_ta_bin(cb3e5ba0-adf1-11e0-998b-0002a5d5c51b) + # E/TC:?? 0 ldelf_init_with_ldelf:131 ldelf failed with res: 0xffff0008 + # --> Add cb3e5ba0-adf1-11e0-998b-0002a5d5c51b.ta into list and repeat + # + # B) From OP-TEE's xtest sources https://github.com/OP-TEE/optee_test + # - Navigate into optee_test repo and run + # $ find ta -path ta/supp_plugin -prune -o -name Makefile -exec grep -oP 'BINARY = \K.*' {} \; + # --> Above comaand produces a list of TAs UUID + # --> It does not produce all UUID due some of them are hardcode into source files + # --> It produce more TA than needed + # + # C) At "find ./out -name "*.ta"" into opteeXtest derivation installPhase + # and uild package with "-L"-flag + # --> Scroll output until find TAs + # ./out/ta/crypt/cb3e5ba0-adf1-11e0-998b-0002a5d5c51b.ta + # ./out/ta/concurrent_large/5ce0c432-0ab0-40e5-a056-782ca0e6aba2.ta + # + # Below list used option C - "cb3e5ba0-adf1-11e0-998b-0002a5d5c51b.ta" - "5ce0c432-0ab0-40e5-a056-782ca0e6aba2.ta" - "e626662e-c0e2-485c-b8c8-09fbce6edf3d.ta" - "c3f6e2c0-3548-11e1-b86c-0800200c9a66.ta" - "873bcd08-c2c3-11e6-a937-d0bf9c45c61c.ta" - "b689f2a7-8adf-477a-9f99-32e90c0ad0a2.ta" - "a4c04d50-f180-11e8-8eb2-f2801f1b9fd1.ta" - "25497083-a58a-4fc5-8a72-1ad7b69b8562.ta" - "731e279e-aafb-4575-a771-38caa6f0cca6.ta" - "5b9e0e40-2636-11e1-ad9e-0002a5d5c51b.ta" - "380231ac-fb99-47ad-a689-9e017eb6e78a.ta" - "d17f73a0-36ef-11e1-984a-0002a5d5c51b.ta" - "614789f2-39c0-4ebf-b235-92b32ac107ed.ta" - "e6a33ed4-562b-463a-bb7e-ff5e15a493c8.ta" - "e13010e0-2ae1-11e5-896a-0002a5d5c51b.ta" - "528938ce-fc59-11e8-8eb2-f2801f1b9fd1.ta" - "ffd2bded-ab7d-4988-95ee-e4962fff7154.ta" - "b3091a65-9751-4784-abf7-0298a7cc35ba.ta" - "f157cda0-550c-11e5-a6fa-0002a5d5c51b.ta" - ]; - pkcs11TaPath = { - name = "fd02c9da-306c-48c7-a49c-bbd827ae86ee.ta"; - path = "${pcks11Ta}/fd02c9da-306c-48c7-a49c-bbd827ae86ee.ta"; - }; - paths = - lib.optionals config.ghaf.hardware.nvidia.orin.optee.xtest xTestTaPaths - ++ lib.optional config.ghaf.hardware.nvidia.orin.optee.pkcs11.enable pkcs11TaPath; - in [(pkgs.linkFarm "optee-load-path" paths)]; + "cb3e5ba0-adf1-11e0-998b-0002a5d5c51b.ta" + "5ce0c432-0ab0-40e5-a056-782ca0e6aba2.ta" + "e626662e-c0e2-485c-b8c8-09fbce6edf3d.ta" + "c3f6e2c0-3548-11e1-b86c-0800200c9a66.ta" + "873bcd08-c2c3-11e6-a937-d0bf9c45c61c.ta" + "b689f2a7-8adf-477a-9f99-32e90c0ad0a2.ta" + "a4c04d50-f180-11e8-8eb2-f2801f1b9fd1.ta" + "25497083-a58a-4fc5-8a72-1ad7b69b8562.ta" + "731e279e-aafb-4575-a771-38caa6f0cca6.ta" + "5b9e0e40-2636-11e1-ad9e-0002a5d5c51b.ta" + "380231ac-fb99-47ad-a689-9e017eb6e78a.ta" + "d17f73a0-36ef-11e1-984a-0002a5d5c51b.ta" + "614789f2-39c0-4ebf-b235-92b32ac107ed.ta" + "e6a33ed4-562b-463a-bb7e-ff5e15a493c8.ta" + "e13010e0-2ae1-11e5-896a-0002a5d5c51b.ta" + "528938ce-fc59-11e8-8eb2-f2801f1b9fd1.ta" + "ffd2bded-ab7d-4988-95ee-e4962fff7154.ta" + "b3091a65-9751-4784-abf7-0298a7cc35ba.ta" + "f157cda0-550c-11e5-a6fa-0002a5d5c51b.ta" + "5c206987-16a3-59cc-ab0f-64b9cfc9e758.ta" + "a720ccbb-51da-417d-b82e-e5445d474a7a.ta" + ]; + pkcs11TaPath = { + name = "fd02c9da-306c-48c7-a49c-bbd827ae86ee.ta"; + path = "${pcks11Ta}/fd02c9da-306c-48c7-a49c-bbd827ae86ee.ta"; + }; + paths = + lib.optionals config.ghaf.hardware.nvidia.orin.optee.xtest xTestTaPaths + ++ lib.optional config.ghaf.hardware.nvidia.orin.optee.pkcs11.enable pkcs11TaPath; + in + [ (pkgs.linkFarm "optee-load-path" paths) ]; environment.systemPackages = (lib.optional config.ghaf.hardware.nvidia.orin.optee.pkcs11-tool pkcs11-tool-optee) diff --git a/targets/vm/flake-module.nix b/targets/vm/flake-module.nix index 992e611aa..13091b366 100644 --- a/targets/vm/flake-module.nix +++ b/targets/vm/flake-module.nix @@ -5,58 +5,69 @@ lib, self, ... -}: let - inherit (inputs) microvm nixos-generators; +}: +let + inherit (inputs) nixos-generators; name = "vm"; system = "x86_64-linux"; - vm = variant: let - hostConfiguration = lib.nixosSystem { - inherit system; - modules = [ - microvm.nixosModules.host - nixos-generators.nixosModules.vm - self.nixosModules.common - self.nixosModules.desktop - self.nixosModules.host - self.nixosModules.microvm + vm = + variant: + let + hostConfiguration = lib.nixosSystem { + inherit system; + modules = [ + nixos-generators.nixosModules.vm + self.nixosModules.common + self.nixosModules.desktop + self.nixosModules.host + self.nixosModules.microvm + self.nixosModules.hw-x86_64-generic - { - ghaf = { - hardware.x86_64.common.enable = true; + { + ghaf = { + hardware.x86_64.common.enable = true; - virtualization.microvm-host.enable = true; - virtualization.microvm-host.networkSupport = true; - host.networking.enable = true; - # TODO: NetVM enabled, but it does not include anything specific - # for this Virtual Machine target - virtualization.microvm.netvm.enable = true; + virtualization = { + microvm-host = { + enable = true; + networkSupport = true; + }; - # Enable all the default UI applications - profiles = { - applications.enable = true; - release.enable = variant == "release"; - debug.enable = variant == "debug"; + # TODO: NetVM enabled, but it does not include anything specific + # for this Virtual Machine target + microvm.netvm.enable = true; + }; + + host.networking.enable = true; + + # Enable all the default UI applications + profiles = { + applications.enable = true; + release.enable = variant == "release"; + debug.enable = variant == "debug"; + }; }; - }; - } - ]; + } + ]; + }; + in + { + inherit hostConfiguration; + name = "${name}-${variant}"; + package = hostConfiguration.config.system.build.${hostConfiguration.config.formatAttr}; }; - in { - inherit hostConfiguration; - name = "${name}-${variant}"; - package = hostConfiguration.config.system.build.${hostConfiguration.config.formatAttr}; - }; targets = [ (vm "debug") (vm "release") ]; -in { +in +{ flake = { - nixosConfigurations = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.hostConfiguration) targets); + nixosConfigurations = builtins.listToAttrs ( + map (t: lib.nameValuePair t.name t.hostConfiguration) targets + ); packages = { - x86_64-linux = - builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); + x86_64-linux = builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets); }; }; } diff --git a/templates/boilerplate/.gitignore b/templates/boilerplate/.gitignore new file mode 100644 index 000000000..50c1eb8d5 --- /dev/null +++ b/templates/boilerplate/.gitignore @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2022-2023 TII (SSRC) and the Ghaf contributors +# +# SPDX-License-Identifier: CC-BY-SA-4.0 +# SPDX-License-Identifier: CC0-1.0 + +result* +ghaf-host.qcow2 +.direnv/ +.idea +linux-*/ diff --git a/templates/boilerplate/default.nix b/templates/boilerplate/default.nix new file mode 100644 index 000000000..4f8ffb1a0 --- /dev/null +++ b/templates/boilerplate/default.nix @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2023 Technology Innovation Institute (TII) +# SPDX-FileCopyrightText: 2020-2023 Eelco Dolstra and the flake-compat contributors +# +# SPDX-License-Identifier: MIT +# This file originates from: +# https://github.com/nix-community/flake-compat +# This file provides backward compatibility to nix < 2.4 clients +{ + system ? builtins.currentSystem, +}: +let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + + inherit (lock.nodes.flake-compat.locked) + owner + repo + rev + narHash + ; + + flake-compat = fetchTarball { + url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; + sha256 = narHash; + }; + + flake = import flake-compat { + inherit system; + src = ./.; + }; +in +flake.defaultNix diff --git a/templates/boilerplate/flake.nix b/templates/boilerplate/flake.nix new file mode 100644 index 000000000..a43b2303c --- /dev/null +++ b/templates/boilerplate/flake.nix @@ -0,0 +1,113 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + description = "Ghaf derived NixOS configuration"; + + nixConfig = { + substituters = [ + "https://cache.vedenemo.dev" + "https://cache.ssrcdevops.tii.ae" + "https://ghaf-dev.cachix.org" + "https://cache.nixos.org/" + ]; + extra-trusted-substituters = [ + "https://cache.vedenemo.dev" + "https://cache.ssrcdevops.tii.ae" + "https://ghaf-dev.cachix.org" + "https://cache.nixos.org/" + ]; + extra-trusted-public-keys = [ + "cache.vedenemo.dev:8NhplARANhClUSWJyLVk4WMyy1Wb4rhmWW2u8AejH9E=" + "cache.ssrcdevops.tii.ae:oOrzj9iCppf+me5/3sN/BxEkp5SaFkHfKTPPZ97xXQk=" + "ghaf-dev.cachix.org-1:S3M8x3no8LFQPBfHw1jl6nmP8A7cVWKntoMKN3IsEQY=" + "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" + ]; + }; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + ghaf = { + url = "github:tiiuae/ghaf"; + # do not over ride the ghaf nixpkgs as it is sometimes following a custom branch + # and will likely break horribly + }; + + # + # Flake and repo structuring configurations + # + # Allows us to structure the flake with the NixOS module system + flake-parts = { + url = "github:hercules-ci/flake-parts"; + inputs.nixpkgs-lib.follows = "nixpkgs"; + }; + + flake-root.url = "github:srid/flake-root"; + + # Format all the things + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # For preserving compatibility with non-Flake users + flake-compat = { + url = "github:nix-community/flake-compat"; + flake = false; + }; + + nix-fast-build = { + url = "github:Mic92/nix-fast-build"; + inputs = { + flake-parts.follows = "flake-parts"; + nixpkgs.follows = "nixpkgs"; + treefmt-nix.follows = "treefmt-nix"; + }; + }; + + # Dependencies used by other inputs + systems.url = "github:nix-systems/default"; + devshell = { + url = "github:numtide/devshell"; + inputs = { + nixpkgs.follows = "nixpkgs"; + }; + }; + + # + # Target Building and services + # + disko = { + url = "github:nix-community/disko/"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + inputs@{ flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + # Toggle this to allow debugging in the repl + # see:https://flake.parts/debug + debug = false; + + systems = [ + "x86_64-linux" + "aarch64-linux" + # RISC-V is a target built from cross compilation and is not + # included as a host build possibility at this point + # Future HW permitting this can be re-evaluated + #"riscv64-linux" + ]; + + imports = [ + ./overlays/flake-module.nix + ./modules/flake-module.nix + ./nix/flake-module.nix + ./packages/flake-module.nix + ./targets/flake-module.nix + ./hydrajobs/flake-module.nix + inputs.flake-root.flakeModule + inputs.treefmt-nix.flakeModule + ]; + }; +} diff --git a/templates/boilerplate/hydrajobs/flake-module.nix b/templates/boilerplate/hydrajobs/flake-module.nix new file mode 100644 index 000000000..7655fa3ee --- /dev/null +++ b/templates/boilerplate/hydrajobs/flake-module.nix @@ -0,0 +1,3 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +_: { flake.hydraJobs = { }; } diff --git a/templates/boilerplate/modules/flake-module.nix b/templates/boilerplate/modules/flake-module.nix new file mode 100644 index 000000000..6174729f4 --- /dev/null +++ b/templates/boilerplate/modules/flake-module.nix @@ -0,0 +1,10 @@ +# Copyright 2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# Modules to be exported from Flake +# +_: { + imports = [ ]; + + flake.nixosModules = { }; +} diff --git a/templates/boilerplate/modules/hardware/default.nix b/templates/boilerplate/modules/hardware/default.nix new file mode 100644 index 000000000..982744d36 --- /dev/null +++ b/templates/boilerplate/modules/hardware/default.nix @@ -0,0 +1,152 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + name = "HARDWARE-NAME definition"; + + # All of the following fields should be replaced with the actual values + # that are dependent on the hardware that you wish to run ghaf on. + # the following tools can help you determine the values that you need + # lspci + # lsblk + # lsusb + # udevadm info + # networkctl list + # + host = { + kernelConfig.kernelParams = [ + "intel_iommu=on,sm_on" + "iommu=pt" + "module_blacklist=i915" # Prevent i915 module from being accidentally used by host + "acpi_backlight=vendor" + "acpi_osi=linux" + ]; + }; + + input = { + keyboard = { + name = [ "AT Translated Set 2 keyboard" ]; + evdev = [ "/dev/input/by-path/platform-i8042-serio-0-event-kbd" ]; + }; + + mouse = { + name = [ + [ + "ELAN067C:00 04F3:31F9 Mouse" + "SYNA8016:00 06CB:CEB3 Mouse" + "ELAN067B:00 04F3:31F8 Mouse" + ] + ]; + evdev = [ "/dev/mouse0" ]; + }; + + touchpad = { + name = [ + [ + "ELAN067C:00 04F3:31F9 Touchpad" + "SYNA8016:00 06CB:CEB3 Touchpad" + "ELAN067B:00 04F3:31F8 Touchpad" + ] + ]; + evdev = [ "/dev/touchpad0" ]; + }; + + misc = { + name = [ + "ThinkPad Extra Buttons" + "TPPS/2 Elan TrackPoint" + ]; + evdev = [ + "/dev/input/by-path/platform-i8042-serio-1-event-mouse" + "/dev/input/by-path/platform-thinkpad_acpi-event" + ]; + }; + }; + + disks = { + disk1.device = "/dev/nvme0n1"; + }; + + network.pciDevices = [ + { + # Passthrough Intel WiFi card + path = "0000:00:14.3"; + vendorId = "8086"; + productId = "51f1"; + name = "wlp0s5f0"; + } + ]; + + gpu = { + pciDevices = [ + { + # Passthrough Intel Iris GPU + path = "0000:00:02.0"; + vendorId = "8086"; + productId = "a7a1"; + } + ]; + kernelConfig = { + stage1.kernelModules = [ "i915" ]; + kernelParams = [ "earlykms" ]; + }; + }; + + # With the current implementation, the whole PCI IOMMU group 14: + # 00:1f.x in the example from Lenovo X1 Carbon + # must be defined for passthrough to AudioVM + audio = { + pciDevices = [ + { + # ISA bridge: Intel Corporation Raptor Lake LPC/eSPI Controller (rev 01) + path = "0000:00:1f.0"; + vendorId = "8086"; + productId = "519d"; + } + { + # Audio device: Intel Corporation Raptor Lake-P/U/H cAVS (rev 01) + path = "0000:00:1f.3"; + vendorId = "8086"; + productId = "51ca"; + } + { + # SMBus: Intel Corporation Alder Lake PCH-P SMBus Host Controller (rev 01) + path = "0000:00:1f.4"; + vendorId = "8086"; + productId = "51a3"; + } + { + # Serial bus controller: Intel Corporation Alder Lake-P PCH SPI Controller (rev 01) + path = "0000:00:1f.5"; + vendorId = "8086"; + productId = "51a4"; + } + ]; + kernelConfig.kernelParams = [ + "snd_intel_dspcfg.dsp_driver=3" + "snd_sof_intel_hda_common.dmic_num=4" + ]; + }; + + usb = { + internal = [ + { + name = "cam0"; + hostbus = "3"; + hostport = "8"; + } + { + name = "fpr0"; + hostbus = "3"; + hostport = "6"; + } + ]; + external = [ + { + name = "gps0"; + vendorId = "067b"; + productId = "23a3"; + } + ]; + }; +} diff --git a/templates/boilerplate/nix/checks.nix b/templates/boilerplate/nix/checks.nix new file mode 100644 index 000000000..1f6e264b9 --- /dev/null +++ b/templates/boilerplate/nix/checks.nix @@ -0,0 +1,16 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + perSystem = + { pkgs, ... }: + { + checks = { + reuse = pkgs.runCommandLocal "reuse-lint" { buildInputs = [ pkgs.reuse ]; } '' + cd ${../.} + reuse lint + touch $out + ''; + }; + # // (lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages); + }; +} diff --git a/templates/boilerplate/nix/devshell.nix b/templates/boilerplate/nix/devshell.nix new file mode 100644 index 000000000..c2b4d71de --- /dev/null +++ b/templates/boilerplate/nix/devshell.nix @@ -0,0 +1,34 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + imports = [ ]; + perSystem = + { + pkgs, + inputs', + lib, + ... + }: + { + devShells.default = pkgs.mkShell { + name = "Ghaf derived devshell"; + packages = + builtins.attrValues { + inherit (pkgs) + alejandra + git + mdbook + nix + nixci + nixos-rebuild + nix-output-monitor + nix-tree + reuse + statix + ; + } + ++ [ inputs'.nix-fast-build.packages.default ] + ++ lib.optional (pkgs.hostPlatform.system != "riscv64-linux") pkgs.cachix; + }; + }; +} diff --git a/templates/boilerplate/nix/flake-module.nix b/templates/boilerplate/nix/flake-module.nix new file mode 100644 index 000000000..3cde3a865 --- /dev/null +++ b/templates/boilerplate/nix/flake-module.nix @@ -0,0 +1,10 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + imports = [ + ./checks.nix + ./devshell.nix + ./nixpkgs.nix + ./treefmt.nix + ]; +} diff --git a/templates/boilerplate/nix/nixpkgs.nix b/templates/boilerplate/nix/nixpkgs.nix new file mode 100644 index 000000000..fecabf1a0 --- /dev/null +++ b/templates/boilerplate/nix/nixpkgs.nix @@ -0,0 +1,18 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ lib, inputs, ... }: +{ + perSystem = + { system, ... }: + { + # customise pkgs + _module.args.pkgs = import inputs.nixpkgs { + inherit system inputs; + config = { + allowUnfree = true; + }; + }; + # make custom top-level lib available to all `perSystem` functions + _module.args.lib = lib; + }; +} diff --git a/templates/boilerplate/nix/treefmt.nix b/templates/boilerplate/nix/treefmt.nix new file mode 100644 index 000000000..6483a9b4f --- /dev/null +++ b/templates/boilerplate/nix/treefmt.nix @@ -0,0 +1,35 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + perSystem = + { config, pkgs, ... }: + { + treefmt.config = { + package = pkgs.treefmt; + inherit (config.flake-root) projectRootFile; + + programs = { + # Nix + alejandra.enable = true; # nix formatter https://github.com/kamadorueda/alejandra + deadnix.enable = true; # removes dead nix code https://github.com/astro/deadnix + statix.enable = true; # prevents use of nix anti-patterns https://github.com/nerdypepper/statix + + # Python + # It was found out that the best outcome comes from running mulitple + # formatters. + black.enable = true; # The Classic Python formatter + isort.enable = true; # Python import sorter + # Ruff, a Python formatter written in Rust (30x faster than Black). + # Also provides additional linting. + # Do not enable ruff.format = true; because then it won't complaing + # about linting errors. The default mode is the check mode. + ruff.check = true; + + # Bash + shellcheck.enable = true; # lints shell scripts https://github.com/koalaman/shellcheck + }; + }; + + formatter = config.treefmt.build.wrapper; + }; +} diff --git a/templates/boilerplate/overlays/README.md b/templates/boilerplate/overlays/README.md new file mode 100644 index 000000000..769076456 --- /dev/null +++ b/templates/boilerplate/overlays/README.md @@ -0,0 +1,40 @@ + + +# Custom packages overlay + +This overlay is for custom packages - new packages, like Gala, or +fixed/adjusted packages from nixpkgs. The overlay might be used as +an example and starting point for any other overlays. + +# Cross-compilation overlay + +This overlay is for fixes regarding cross-compilation. It is maintained as a +separate overlay, because some of the changes might trigger heavy rebuilds of +packages in nixpkgs. It can then be separately added to cross-compilation +builds. + +## General Requirements + +Use final/prev pair in your overlays instead of other variations +since it looks more logical: +previous (unmodified) package vs final (finalazed, adjusted) package. + +Use deps[X][Y] variations instead of juggling dependencies between +nativeBuildInputs and buildInputs where possible. +It makes things clear and robust. + +# Upstream PR and commit tracking + +Some patches are carried as overlays and others are patches that are cherry-picked +from staging and main into a tiiuae maintained version of nixpkgs + +The status of the integration in nixpkgs can be tracked using the [Pull Request Tracker](https://nixpk.gs/pr-tracker.html) + +## From Overlays + + +## carried in tiiuae/nixpkgs/ diff --git a/templates/boilerplate/overlays/cross-compilation/default.nix b/templates/boilerplate/overlays/cross-compilation/default.nix new file mode 100644 index 000000000..bd0a3dc74 --- /dev/null +++ b/templates/boilerplate/overlays/cross-compilation/default.nix @@ -0,0 +1,8 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# This overlay is for specific fixes needed only to enable cross-compilation. +# +(_final: _prev: { + #some-overlay = import ./some-package {inherit prev;}; +}) diff --git a/templates/boilerplate/overlays/custom-packages/default.nix b/templates/boilerplate/overlays/custom-packages/default.nix new file mode 100644 index 000000000..5d332d0fb --- /dev/null +++ b/templates/boilerplate/overlays/custom-packages/default.nix @@ -0,0 +1,9 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# This overlay patches packages in nixpkgs, and adds in some of the ghaf's +# packages. +# +(_final: _prev: { + #some-package = import ./some-package {inherit prev;}; +}) diff --git a/templates/boilerplate/overlays/flake-module.nix b/templates/boilerplate/overlays/flake-module.nix new file mode 100644 index 000000000..46aabf6be --- /dev/null +++ b/templates/boilerplate/overlays/flake-module.nix @@ -0,0 +1,10 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# Flake module for exporting overlays +{ + flake.overlays = { + cross-compilation = import ./cross-compilation; + custom-packages = import ./custom-packages; + }; +} diff --git a/templates/boilerplate/packages/flake-module.nix b/templates/boilerplate/packages/flake-module.nix new file mode 100644 index 000000000..2a44c2515 --- /dev/null +++ b/templates/boilerplate/packages/flake-module.nix @@ -0,0 +1,9 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +_: { + perSystem = _: { + # packages = self.lib.platformPkgs system { + #some-package = callPackage ./some-package {}; + # }; + }; +} diff --git a/templates/boilerplate/shell.nix b/templates/boilerplate/shell.nix new file mode 100644 index 000000000..e5368b16b --- /dev/null +++ b/templates/boilerplate/shell.nix @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2023 Technology Innovation Institute (TII) +# SPDX-FileCopyrightText: 2020-2023 Eelco Dolstra and the flake-compat contributors +# +# SPDX-License-Identifier: MIT +# This file originates from: +# https://github.com/nix-community/flake-compat +# This file provides backward compatibility to nix < 2.4 clients +{ + system ? builtins.currentSystem, +}: +let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + + inherit (lock.nodes.flake-compat.locked) + owner + repo + rev + narHash + ; + + flake-compat = fetchTarball { + url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; + sha256 = narHash; + }; + + flake = import flake-compat { + inherit system; + src = ./.; + }; +in +flake.shellNix diff --git a/templates/boilerplate/targets/flake-module.nix b/templates/boilerplate/targets/flake-module.nix new file mode 100644 index 000000000..bb568bee9 --- /dev/null +++ b/templates/boilerplate/targets/flake-module.nix @@ -0,0 +1,3 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +_: { } diff --git a/templates/flake-module.nix b/templates/flake-module.nix index 9793cac0c..1601f868f 100644 --- a/templates/flake-module.nix +++ b/templates/flake-module.nix @@ -1,7 +1,6 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 { - # TODO rework the templates to match the new modules flake.templates = { # Module template ghaf-module = { @@ -9,33 +8,10 @@ description = "A config to bootstrap a Ghaf compatible module"; }; - # A Selection of targets that utilize Ghaf to define more feature rich - # projects/products. - - # ARM targets - target-aarch64-nvidia-orin-agx = { - path = ./targets/aarch64/nvidia/orin-agx; - description = "A Ghaf based configuration for the Nvidia Orin AGX"; - }; - target-aarch64-nvidia-orin-nx = { - path = ./targets/aarch64/nvidia/orin-nx; - description = "A Ghaf based configuration for the Nvidia Orin NX"; - }; - target-aarch64-nxp-imx8 = { - path = ./targets/aarch64/nxp/imx8; - description = "A Ghaf based configuration for the NXP iMX8"; - }; - - # x86 targets - target-x86_64-generic = { - path = ./targets/x86_64/generic; - description = "A Ghaf based configuration for x86_64 targets"; - }; - - # RISC-v targets - target-riscv64-microchip-polarfire = { - path = ./targets/riscv64/microchip/polarfire; - description = "A Ghaf based configuration for the Microchip Polarfire"; + # Boilerplate for a derived project that uses the Ghaf framework + target-boilerplate = { + path = ./boilerplate; + description = "Some boilerplate code to get you started"; }; }; } diff --git a/templates/modules/default.nix b/templates/modules/default.nix index dd0d597c0..0614f7712 100644 --- a/templates/modules/default.nix +++ b/templates/modules/default.nix @@ -6,24 +6,19 @@ # # https://nixos.org/manual/nixos/stable/index.html#sec-writing-modules # -{ - config, - options, - lib, - ... -}: let +{ config, lib, ... }: +let # inherit (builtins) A B C; # inherit (lib) D E F; # inherit (lib.ghaf) G H I; cfg = config.ghaf.X.Y; in - with lib; { - imports = [ - ]; +{ + imports = [ ]; - options.ghaf.X.Y = { - enable = mkEnableOption "Option"; - }; + options.ghaf.X.Y = { + enable = lib.mkEnableOption "Option"; + }; - config = mkIf cfg.enable {}; - } + config = lib.mkIf cfg.enable { }; +} diff --git a/templates/targets/aarch64/nvidia/orin-agx/flake.nix b/templates/targets/aarch64/nvidia/orin-agx/flake.nix deleted file mode 100644 index 68f640d00..000000000 --- a/templates/targets/aarch64/nvidia/orin-agx/flake.nix +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - description = " - Ghaf based configuration"; - - nixConfig = { - substituters = [ - "https://cache.vedenemo.dev" - "https://cache.ssrcdevops.tii.ae" - "https://ghaf-dev.cachix.org" - "https://cache.nixos.org/" - ]; - extra-trusted-substituters = [ - "https://cache.vedenemo.dev" - "https://cache.ssrcdevops.tii.ae" - "https://ghaf-dev.cachix.org" - "https://cache.nixos.org/" - ]; - extra-trusted-public-keys = [ - "cache.vedenemo.dev:8NhplARANhClUSWJyLVk4WMyy1Wb4rhmWW2u8AejH9E=" - "cache.ssrcdevops.tii.ae:oOrzj9iCppf+me5/3sN/BxEkp5SaFkHfKTPPZ97xXQk=" - "ghaf-dev.cachix.org-1:S3M8x3no8LFQPBfHw1jl6nmP8A7cVWKntoMKN3IsEQY=" - "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" - ]; - }; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11"; - flake-utils.url = "github:numtide/flake-utils"; - jetpack-nixos = { - url = "github:anduril/jetpack-nixos"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - ghaf = { - url = "github:tiiuae/ghaf"; - inputs = { - nixpkgs.follows = "nixpkgs"; - flake-utils.follows = "flake-utils"; - jetpack-nixos.follows = "jetpack-nixos"; - }; - }; - }; - - outputs = { - self, - ghaf, - nixpkgs, - jetpack-nixos, - flake-utils, - ... - }: let - systems = with flake-utils.lib.system; [ - x86_64-linux - aarch64-linux - ]; - mkFlashScript = import (ghaf + "/lib/mk-flash-script"); - in - # Combine list of attribute sets together - nixpkgs.lib.foldr nixpkgs.lib.recursiveUpdate {} [ - (flake-utils.lib.eachSystem systems (system: { - formatter = nixpkgs.legacyPackages.${system}.alejandra; - })) - - { - nixosConfigurations.PROJ_NAME-ghaf-debug = ghaf.nixosConfigurations.nvidia-jetson-orin-agx-debug.extendModules { - modules = [ - { - #insert your additional modules here e.g. - # virtualisation.docker.enable = true; - # users.users."ghaf".extraGroups = ["docker"]; - } - ]; - }; - packages.aarch64-linux.PROJ_NAME-ghaf-debug = self.nixosConfigurations.PROJ_NAME-ghaf-debug.config.system.build.${self.nixosConfigurations.PROJ_NAME-ghaf-debug.config.formatAttr}; - - packages.x86_64-linux.PROJ_NAME-ghaf-debug-flash-script = mkFlashScript { - inherit nixpkgs jetpack-nixos; - hostConfiguration = self.nixosConfigurations.PROJ_NAME-ghaf-debug; - flash-tools-system = flake-utils.lib.system.x86_64-linux; - }; - } - ]; -} diff --git a/templates/targets/aarch64/nvidia/orin-nx/flake.nix b/templates/targets/aarch64/nvidia/orin-nx/flake.nix deleted file mode 100644 index dd0cb2552..000000000 --- a/templates/targets/aarch64/nvidia/orin-nx/flake.nix +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - description = "PROJ_NAME - Ghaf based configuration"; - - nixConfig = { - substituters = [ - "https://cache.vedenemo.dev" - "https://cache.ssrcdevops.tii.ae" - "https://ghaf-dev.cachix.org" - "https://cache.nixos.org/" - ]; - extra-trusted-substituters = [ - "https://cache.vedenemo.dev" - "https://cache.ssrcdevops.tii.ae" - "https://ghaf-dev.cachix.org" - "https://cache.nixos.org/" - ]; - extra-trusted-public-keys = [ - "cache.vedenemo.dev:8NhplARANhClUSWJyLVk4WMyy1Wb4rhmWW2u8AejH9E=" - "cache.ssrcdevops.tii.ae:oOrzj9iCppf+me5/3sN/BxEkp5SaFkHfKTPPZ97xXQk=" - "ghaf-dev.cachix.org-1:S3M8x3no8LFQPBfHw1jl6nmP8A7cVWKntoMKN3IsEQY=" - "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" - ]; - }; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11"; - flake-utils.url = "github:numtide/flake-utils"; - jetpack-nixos = { - url = "github:anduril/jetpack-nixos"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - ghaf = { - url = "github:tiiuae/ghaf"; - inputs = { - nixpkgs.follows = "nixpkgs"; - flake-utils.follows = "flake-utils"; - jetpack-nixos.follows = "jetpack-nixos"; - }; - }; - }; - - outputs = { - self, - ghaf, - nixpkgs, - jetpack-nixos, - flake-utils, - ... - }: let - systems = with flake-utils.lib.system; [ - x86_64-linux - aarch64-linux - ]; - mkFlashScript = import (ghaf + "/lib/mk-flash-script"); - in - # Combine list of attribute sets together - nixpkgs.lib.foldr nixpkgs.lib.recursiveUpdate {} [ - (flake-utils.lib.eachSystem systems (system: { - formatter = nixpkgs.legacyPackages.${system}.alejandra; - })) - - { - nixosConfigurations.PROJ_NAME-ghaf-debug = ghaf.nixosConfigurations.nvidia-jetson-orin-nx-debug.extendModules { - modules = [ - { - #insert your additional modules here e.g. - # virtualisation.docker.enable = true; - # users.users."ghaf".extraGroups = ["docker"]; - } - ]; - }; - packages.aarch64-linux.PROJ_NAME-ghaf-debug = self.nixosConfigurations.PROJ_NAME-ghaf-debug.config.system.build.${self.nixosConfigurations.PROJ_NAME-ghaf-debug.config.formatAttr}; - - packages.x86_64-linux.PROJ_NAME-ghaf-debug-flash-script = mkFlashScript { - inherit nixpkgs jetpack-nixos; - hostConfiguration = self.nixosConfigurations.PROJ_NAME-ghaf-debug; - flash-tools-system = flake-utils.lib.system.x86_64-linux; - }; - } - ]; -} diff --git a/templates/targets/aarch64/nxp/imx8/flake.nix b/templates/targets/aarch64/nxp/imx8/flake.nix deleted file mode 100644 index 1c762c9ef..000000000 --- a/templates/targets/aarch64/nxp/imx8/flake.nix +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - description = "PROJ_NAME - Ghaf based configuration"; - - nixConfig = { - substituters = [ - "https://cache.vedenemo.dev" - "https://cache.ssrcdevops.tii.ae" - "https://ghaf-dev.cachix.org" - "https://cache.nixos.org/" - ]; - extra-trusted-substituters = [ - "https://cache.vedenemo.dev" - "https://cache.ssrcdevops.tii.ae" - "https://ghaf-dev.cachix.org" - "https://cache.nixos.org/" - ]; - extra-trusted-public-keys = [ - "cache.vedenemo.dev:8NhplARANhClUSWJyLVk4WMyy1Wb4rhmWW2u8AejH9E=" - "cache.ssrcdevops.tii.ae:oOrzj9iCppf+me5/3sN/BxEkp5SaFkHfKTPPZ97xXQk=" - "ghaf-dev.cachix.org-1:S3M8x3no8LFQPBfHw1jl6nmP8A7cVWKntoMKN3IsEQY=" - "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" - ]; - }; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11"; - flake-utils.url = "github:numtide/flake-utils"; - nixos-hardware.url = "github:nixos/nixos-hardware"; - ghaf = { - url = "github:tiiuae/ghaf"; - inputs = { - nixpkgs.follows = "nixpkgs"; - flake-utils.follows = "flake-utils"; - nixos-hardware.follows = "nixos-hardware"; - }; - }; - }; - - outputs = { - self, - ghaf, - nixpkgs, - flake-utils, - ... - }: let - systems = with flake-utils.lib.system; [ - x86_64-linux - aarch64-linux - ]; - in - # Combine list of attribute sets together - nixpkgs.lib.foldr nixpkgs.lib.recursiveUpdate {} [ - (flake-utils.lib.eachSystem systems (system: { - formatter = nixpkgs.legacyPackages.${system}.alejandra; - })) - - { - nixosConfigurations.PROJ_NAME-ghaf-debug = ghaf.nixosConfigurations.imx8qm-mek-debug.extendModules { - modules = [ - { - #insert your additional modules here e.g. - # virtualisation.docker.enable = true; - # users.users."ghaf".extraGroups = ["docker"]; - } - ]; - }; - packages.aarch64-linux.PROJ_NAME-ghaf-debug = self.nixosConfigurations.PROJ_NAME-ghaf-debug.config.system.build.${self.nixosConfigurations.PROJ_NAME-ghaf-debug.config.formatAttr}; - } - ]; -} diff --git a/templates/targets/riscv64/microchip/polarfire/flake.nix b/templates/targets/riscv64/microchip/polarfire/flake.nix deleted file mode 100644 index ab8314803..000000000 --- a/templates/targets/riscv64/microchip/polarfire/flake.nix +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - description = "PROJ_NAME - Ghaf based configuration"; - - nixConfig = { - substituters = [ - "https://cache.vedenemo.dev" - "https://cache.ssrcdevops.tii.ae" - "https://ghaf-dev.cachix.org" - "https://cache.nixos.org/" - ]; - extra-trusted-substituters = [ - "https://cache.vedenemo.dev" - "https://cache.ssrcdevops.tii.ae" - "https://ghaf-dev.cachix.org" - "https://cache.nixos.org/" - ]; - extra-trusted-public-keys = [ - "cache.vedenemo.dev:8NhplARANhClUSWJyLVk4WMyy1Wb4rhmWW2u8AejH9E=" - "cache.ssrcdevops.tii.ae:oOrzj9iCppf+me5/3sN/BxEkp5SaFkHfKTPPZ97xXQk=" - "ghaf-dev.cachix.org-1:S3M8x3no8LFQPBfHw1jl6nmP8A7cVWKntoMKN3IsEQY=" - "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" - ]; - }; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11"; - flake-utils.url = "github:numtide/flake-utils"; - nixos-hardware.url = "github:NixOS/nixos-hardware"; - ghaf = { - url = "github:tiiuae/ghaf"; - inputs = { - nixpkgs.follows = "nixpkgs"; - flake-utils.follows = "flake-utils"; - nixos-hardware.follows = "nixos-hardware"; - }; - }; - }; - - outputs = { - self, - ghaf, - nixpkgs, - flake-utils, - ... - }: let - systems = with flake-utils.lib.system; [ - x86_64-linux - riscv64-linux - ]; - in - # Combine list of attribute sets together - nixpkgs.lib.foldr nixpkgs.lib.recursiveUpdate {} [ - (flake-utils.lib.eachSystem systems (system: { - formatter = nixpkgs.legacyPackages.${system}.alejandra; - })) - - { - nixosConfigurations.PROJ_NAME-ghaf-debug = ghaf.nixosConfigurations.microchip-icicle-kit-debug.extendModules { - modules = [ - { - #insert your additional modules here e.g. - # virtualisation.docker.enable = true; - # users.users."ghaf".extraGroups = ["docker"]; - } - ({pkgs, ...}: { - environment.systemPackages = with pkgs; [ - #Add additional system packages here - ]; - }) - ]; - }; - packages.riscv64-linux.PROJ_NAME-ghaf-debug = self.nixosConfigurations.PROJ_NAME-ghaf-debug.config.system.build.sdImage; - } - ]; -} diff --git a/templates/targets/x86_64/generic/flake.nix b/templates/targets/x86_64/generic/flake.nix deleted file mode 100644 index 1fc90ce33..000000000 --- a/templates/targets/x86_64/generic/flake.nix +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - description = "PROJ_NAME - Ghaf based configuration"; - - nixConfig = { - substituters = [ - "https://cache.vedenemo.dev" - "https://cache.ssrcdevops.tii.ae" - "https://ghaf-dev.cachix.org" - "https://cache.nixos.org/" - ]; - extra-trusted-substituters = [ - "https://cache.vedenemo.dev" - "https://cache.ssrcdevops.tii.ae" - "https://ghaf-dev.cachix.org" - "https://cache.nixos.org/" - ]; - extra-trusted-public-keys = [ - "cache.vedenemo.dev:8NhplARANhClUSWJyLVk4WMyy1Wb4rhmWW2u8AejH9E=" - "cache.ssrcdevops.tii.ae:oOrzj9iCppf+me5/3sN/BxEkp5SaFkHfKTPPZ97xXQk=" - "ghaf-dev.cachix.org-1:S3M8x3no8LFQPBfHw1jl6nmP8A7cVWKntoMKN3IsEQY=" - "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" - ]; - }; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11"; - flake-utils.url = "github:numtide/flake-utils"; - nixos-hardware.url = "github:nixos/nixos-hardware"; - ghaf = { - url = "github:tiiuae/ghaf"; - inputs = { - nixpkgs.follows = "nixpkgs"; - flake-utils.follows = "flake-utils"; - nixos-hardware.follows = "nixos-hardware"; - }; - }; - }; - - outputs = { - self, - ghaf, - nixpkgs, - flake-utils, - ... - }: let - systems = with flake-utils.lib.system; [ - x86_64-linux - ]; - in - # Combine list of attribute sets together - nixpkgs.lib.foldr nixpkgs.lib.recursiveUpdate {} [ - (flake-utils.lib.eachSystem systems (system: { - formatter = nixpkgs.legacyPackages.${system}.alejandra; - })) - - { - nixosConfigurations.PROJ_NAME-ghaf-debug = ghaf.nixosConfigurations.generic-x86_64-debug.extendModules { - modules = [ - { - #insert your additional modules here e.g. - # virtualisation.docker.enable = true; - # users.users."ghaf".extraGroups = ["docker"]; - - # To handle the majority of laptops we need a little something extra - # TODO:: SEE: https://github.com/NixOS/nixos-hardware/blob/master/flake.nix - # nixos-hardware.nixosModules.lenovo-thinkpad-x1-10th-gen - } - ]; - }; - packages.x86_64-linux.PROJ_NAME-ghaf-debug = self.nixosConfigurations.PROJ_NAME-ghaf-debug.config.system.build.${self.nixosConfigurations.PROJ_NAME-ghaf-debug.config.formatAttr}; - } - ]; -} From 97f198afcd01872839766c6b5cc83547dd93019f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Sandstr=C3=B6m?= Date: Tue, 3 Sep 2024 12:39:33 +0000 Subject: [PATCH 26/26] bugfix: qemu 9.0.2 was not patched and built correctly --- .../virtualization/default.nix | 3 +- .../microvm/virtualization/microvm/gpiovm.nix | 2 +- overlays/custom-packages/default.nix | 17 +- .../qemu/gpio-passthrough-qemu_9_0.nix | 102 ++ .../qemu/qemu-v9.0.2_gpio-virt.patch | 1389 +++++++++++++++++ 5 files changed, 1510 insertions(+), 3 deletions(-) create mode 100644 overlays/custom-packages/qemu/gpio-passthrough-qemu_9_0.nix create mode 100644 overlays/custom-packages/qemu/qemu-v9.0.2_gpio-virt.patch diff --git a/modules/jetpack/nvidia-jetson-orin/virtualization/default.nix b/modules/jetpack/nvidia-jetson-orin/virtualization/default.nix index 1a7f3ee70..e2c48ec74 100644 --- a/modules/jetpack/nvidia-jetson-orin/virtualization/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/virtualization/default.nix @@ -22,7 +22,8 @@ in { config = lib.mkIf cfg.enable { - nixpkgs.overlays = [(import ./overlays/qemu)]; + # removed because we hack the GPIO Qemu in overlays/custom-packages/default.nix + # nixpkgs.overlays = [(import ./overlays/qemu)]; # TODO: Consider are these really needed, maybe add only in debug builds? /* diff --git a/modules/microvm/virtualization/microvm/gpiovm.nix b/modules/microvm/virtualization/microvm/gpiovm.nix index 9a8d37b41..260a42a1e 100644 --- a/modules/microvm/virtualization/microvm/gpiovm.nix +++ b/modules/microvm/virtualization/microvm/gpiovm.nix @@ -173,7 +173,7 @@ let "-sandbox" "on" "-nographic" "-no-reboot" - "-dtb" "${gpioGuestDtb}" + "-dtb ${gpioGuestDtb}" "-kernel" "${guestKernel}" # "-drive" "file=${guestRootFs},if=virtio,format=qcow2" "-machine" "virt,accel=kvm" diff --git a/overlays/custom-packages/default.nix b/overlays/custom-packages/default.nix index 82ef7845d..73dd98955 100644 --- a/overlays/custom-packages/default.nix +++ b/overlays/custom-packages/default.nix @@ -4,13 +4,28 @@ # This overlay patches packages in nixpkgs, and adds in some of the ghaf's # packages. # +/* +{ final, prev, builtins}: +let + # Import and evaluate the QEMU overlays + qemuOverlay1 = import ./qemu { inherit final prev; }; + qemuOverlay2 = import ../../modules/jetpack/nvidia-jetson-orin/virtualization/host/gpio-virt-host/overlays/qemu { inherit final prev; }; + + # Merge the resulting attribute sets + # mergedQemu = builtins.mergeAttrs qemuOverlay1 qemuOverlay2; +in +*/ (final: prev: { gala-app = final.callPackage ../../packages/gala { }; element-desktop = import ./element-desktop { inherit prev; }; element-gps = final.callPackage ../../packages/element-gps { }; element-web = final.callPackage ../../packages/element-web { }; waypipe = import ./waypipe { inherit final prev; }; - qemu_kvm = import ./qemu { inherit final prev; }; + # qemu_kvm = import ./qemu { inherit final prev; }; + # qemu_gpio = import ./qemu/gpio-qemu.nix {inherit final prev;}; + # we have to patch qemu_kvm directly ... for now + # qemu_kvm = builtins.mergeAttrs qemuOverlay1 qemuOverlay2; + qemu_kvm = import ./qemu/gpio-passthrough-qemu_9_0.nix { inherit final prev; }; nm-launcher = final.callPackage ../../packages/nm-launcher { }; icon-pack = final.callPackage ../../packages/icon-pack { }; labwc = import ./labwc { inherit prev; }; diff --git a/overlays/custom-packages/qemu/gpio-passthrough-qemu_9_0.nix b/overlays/custom-packages/qemu/gpio-passthrough-qemu_9_0.nix new file mode 100644 index 000000000..f7a59a3e4 --- /dev/null +++ b/overlays/custom-packages/qemu/gpio-passthrough-qemu_9_0.nix @@ -0,0 +1,102 @@ +# Copyright 2022-2023 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 + +{ final, prev }: +let + pkgs = prev.pkgs; + # builtins = import (import {}); + # builtins = (import {}); + + qemu_version = prev.qemu_kvm.version; + qemu_major = final.lib.versions.major qemu_version; + qemu_minor = final.lib.versions.minor qemu_version; + + qemuGpioPatch = ./qemu-v9.0.2_gpio-virt.patch; + +in + prev.qemu_kvm.overrideAttrs ( + _final: prev: + (final.lib.optionalAttrs (qemu_major == "9" && qemu_minor == "0") { + patches = builtins.trace "Patching Qemu for GPIO passthrough" + prev.patches ++ [ qemuGpioPatch ]; + + /* + buildInputs = with pkgs; + prev.buildInputs ++ + [ + # git util-linux pkg-config autoconf autogen automake flex bison meson ninja cmake json_c gcc gnumake libtool valgrind python3 + # makeWrapper mktemp wayland-protocols + glib dbus pixman zlib bzip2 lzo libgpiod snappy curl libssh libcap libepoxy nettle attr systemd liburing + libdrm SDL2 gtk3 gnutls + libslirp libselinux alsa-lib alsa-oss pulseaudio pipewire acpica-tools pam_p11 pam_u2f vte + libibumad libnfs libseccomp libxkbcommon libcacard libusb1 libaio libcap_ng libtasn1 libgcrypt keyutils canokey-qemu + fuse3 libbpf capstone fdtools vde2 texinfo spice virglrenderer multipath-tools ncurses sealcurses lzfse gsasl xgboost + libvncserver cmocka basez SDL2 SDL2_image ceph gsasl xdp-tools fdtools dtc + ]; + */ + buildInputs = with pkgs; + prev.buildInputs ++ + [ + perl keyutils rng-tools snappy + gtk3 gtk3-x11 ncurses nettle + libepoxy lzfse libcacard libibumad + usbredir libusb1 libusbp libaio libssh libgcrypt libcap libslirp libbpf + gusb libevdev libevdevplus libevdevc libudev-zero libudev0-shim + vte libvncserver gtk-vnc fuse libnfs + SDL2 SDL2_image vte + virglrenderer rutabaga_gfx + spice spice-autorandr spice-gtk spice-protocol spice-up spice-vdagent + iconv libdwg libdrm dbus libgpiod + ]; + + configureFlags = + prev.configureFlags ++ + [ + "--target-list=aarch64-softmmu" + "--disable-strip" + "--disable-docs" + "--disable-spice" + "--enable-tools" + "--localstatedir=/var" + "--sysconfdir=/etc" + # "--cross-prefix=" + "--enable-guest-agent" + "--enable-numa" + "--enable-seccomp" + "--enable-smartcard" + "--enable-usb-redir" + "--enable-linux-aio" + "--enable-tpm" + "--enable-libiscsi" + "--enable-linux-io-uring" + "--enable-canokey" + "--enable-capstone" + + "--enable-gtk" + "--enable-opengl" + "--enable-virglrenderer" + "--enable-sdl" + "--enable-vnc" + "--enable-vnc-jpeg" + "--enable-vde" + "--enable-vhost-net" + "--enable-vhost-user" + # "--prefix=$out" + ]; + + /* + installPhase = '' + make install + ln -s $out/bin/qemu-system-aarch64 $out/bin/qemu-passthrough + ''; + */ + + meta = { + description = "QEMU with passthrough modifications for Ghaf"; + homepage = "https://github.com/KimGSandstrom/qemu-passthrough"; + # license = lib.licenses.apache2; + }; + + }) + # Add additional custom configurations for `qemu_gpio` if needed + ) diff --git a/overlays/custom-packages/qemu/qemu-v9.0.2_gpio-virt.patch b/overlays/custom-packages/qemu/qemu-v9.0.2_gpio-virt.patch new file mode 100644 index 000000000..2a7bffcad --- /dev/null +++ b/overlays/custom-packages/qemu/qemu-v9.0.2_gpio-virt.patch @@ -0,0 +1,1389 @@ +diff --git a/configs/devices/aarch64-softmmu/default.mak b/configs/devices/aarch64-softmmu/default.mak +index f82a04c27d..35dd533d37 100644 +--- a/configs/devices/aarch64-softmmu/default.mak ++++ b/configs/devices/aarch64-softmmu/default.mak +@@ -8,3 +8,6 @@ include ../arm-softmmu/default.mak + # CONFIG_XLNX_ZYNQMP_ARM=n + # CONFIG_XLNX_VERSAL=n + # CONFIG_SBSA_REF=n ++ ++CONFIG_NVIDIA_BPMP_GUEST=y ++CONFIG_NVIDIA_GPIO_GUEST=y +diff --git a/hw/arm/virt.c b/hw/arm/virt.c +index a9a913aead..4d79d3d5ec 100644 +--- a/hw/arm/virt.c ++++ b/hw/arm/virt.c +@@ -83,6 +83,8 @@ + #include "hw/virtio/virtio-md-pci.h" + #include "hw/virtio/virtio-iommu.h" + #include "hw/char/pl011.h" ++#include "hw/misc/nvidia_bpmp_guest.h" ++#include "hw/misc/nvidia_gpio_guest.h" + #include "qemu/guest-random.h" + + static GlobalProperty arm_virt_compat[] = { +@@ -176,6 +178,8 @@ static const MemMapEntry base_memmap[] = { + [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, + [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, + [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, ++ [VIRT_NVIDIA_BPMP_GUEST] = { 0x090c0000, 0x00001000 }, ++ [VIRT_NVIDIA_GPIO_GUEST] = { 0x090c1000, 0x00001000 }, + [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, + /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ + [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, +@@ -1072,6 +1076,12 @@ static void create_virtio_devices(const VirtMachineState *vms) + hwaddr size = vms->memmap[VIRT_MMIO].size; + MachineState *ms = MACHINE(vms); + ++ /* Create NVIDIA BPMP guest passthru device, possibly need update ftd - WIP */ ++ nvidia_bpmp_guest_create(vms->memmap[VIRT_NVIDIA_BPMP_GUEST].base); ++ ++ /* Create NVIDIA GPIO guest passthru device, possibly need update ftd - WIP */ ++ nvidia_gpio_guest_create(vms->memmap[VIRT_NVIDIA_GPIO_GUEST].base); ++ + /* We create the transports in forwards order. Since qbus_realize() + * prepends (not appends) new child buses, the incrementing loop below will + * create a list of virtio-mmio buses with decreasing base addresses. +diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig +index 1e08785b83..0f762a0119 100644 +--- a/hw/misc/Kconfig ++++ b/hw/misc/Kconfig +@@ -11,6 +11,12 @@ config ARMSSE_MHU + config ARMSSE_CPU_PWRCTRL + bool + ++config NVIDIA_BPMP_GUEST ++ bool ++ ++config NVIDIA_GPIO_GUEST ++ bool ++ + config ISA_DEBUG + bool + depends on ISA_BUS +diff --git a/hw/misc/meson.build b/hw/misc/meson.build +index 86596a3888..876417cdec 100644 +--- a/hw/misc/meson.build ++++ b/hw/misc/meson.build +@@ -1,4 +1,6 @@ + system_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c')) ++system_ss.add(when: 'CONFIG_NVIDIA_BPMP_GUEST', if_true: files('nvidia_bpmp_guest.c')) ++system_ss.add(when: 'CONFIG_NVIDIA_GPIO_GUEST', if_true: files('nvidia_gpio_guest.c')) + system_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) + system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) + system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) +diff --git a/hw/misc/nvidia_bpmp_guest.c b/hw/misc/nvidia_bpmp_guest.c +new file mode 100644 +index 0000000000..3facee6d00 +--- /dev/null ++++ b/hw/misc/nvidia_bpmp_guest.c +@@ -0,0 +1,171 @@ ++#include "qemu/osdep.h" ++#include "qemu/log.h" ++#include "qapi/error.h" /* provides error_fatal() handler */ ++#include "hw/sysbus.h" /* provides all sysbus registering func */ ++#include "hw/misc/nvidia_bpmp_guest.h" ++ ++#define TYPE_NVIDIA_BPMP_GUEST "nvidia_bpmp_guest" ++typedef struct NvidiaBpmpGuestState NvidiaBpmpGuestState; ++DECLARE_INSTANCE_CHECKER(NvidiaBpmpGuestState, NVIDIA_BPMP_GUEST, TYPE_NVIDIA_BPMP_GUEST) ++ ++#define TX_BUF 0x0000 ++#define RX_BUF 0x0200 ++#define TX_SIZ 0x0400 ++#define RX_SIZ 0x0408 ++#define RET_COD 0x0410 ++#define MRQ 0x0500 ++ ++#define MEM_SIZE 0x600 ++#define HOST_DEVICE_PATH "/dev/bpmp-host" ++#define MESSAGE_SIZE 0x0200 ++ ++// qemu_log_mask(LOG_UNIMP, "%s: \n", __func__ ); ++ ++struct NvidiaBpmpGuestState ++{ ++ SysBusDevice parent_obj; ++ MemoryRegion iomem; ++ int host_device_fd; ++ uint8_t mem[MEM_SIZE]; ++}; ++ ++// Device memory map: ++ ++// 0x090c0000 + /* Base address, size 0x01000 */ ++ ++// 0x0000 \ Tx buffer ++// 0x01FF / ++// 0x0200 \ Rx buffer ++// 0x03FF / ++// 0x0400 -- Tx size ++// 0x0408 -- Rx size ++// 0x0410 -- Ret ++// 0x0500 -- mrq ++ ++ ++ ++// Data should be aligned to 64bit paragraph. ++ ++// Protocol is: ++// 1. Write data buffers to 0x0000-0x01FF and 0x0200-0x03FF ++// 2. Write buffer sizes to 0x0400 (Tx) and 0x0408 (Rx) ++// 2. Start operation by writing mrq opcode to address 0x0500 ++// 3. Read ret code from 0x0410 and response data from the buffers ++ ++static uint64_t nvidia_bpmp_guest_read(void *opaque, hwaddr addr, unsigned int size) ++{ ++ NvidiaBpmpGuestState *s = opaque; ++ ++ if (addr >= MEM_SIZE) ++ return 0xDEADBEEF; ++ ++ // Cast buffer location as uint64_t ++ return *(uint64_t*)&s->mem[addr]; ++} ++ ++static void nvidia_bpmp_guest_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) ++{ ++ NvidiaBpmpGuestState *s = opaque; ++ int ret; ++ ++ struct ++ { ++ unsigned int mrq; ++ struct ++ { ++ void *data; ++ size_t size; ++ } tx; ++ struct ++ { ++ void *data; ++ size_t size; ++ int ret; ++ } rx; ++ } messg; ++ ++ memset(&messg, 0, sizeof(messg)); ++ ++ if (addr >= MEM_SIZE){ ++ qemu_log_mask(LOG_UNIMP, "qemu: Error addr >= MEM_SIZE in 0x%lX data: 0x%lX\n", addr, data); ++ return; ++ } ++ ++ switch (addr) ++ { ++ case MRQ: ++ // set up the structure ++ messg.mrq = data; ++ messg.tx.data = &s->mem[TX_BUF]; ++ memcpy(&messg.tx.size, &s->mem[TX_SIZ], sizeof(messg.tx.size)); ++ messg.rx.data = &s->mem[RX_BUF]; ++ memcpy(&messg.rx.size, &s->mem[RX_SIZ], sizeof(messg.rx.size)); ++ ++ ret = write(s->host_device_fd, &messg, sizeof(messg)); // Send the data to the host module ++ if (ret < 0) ++ { ++ qemu_log_mask(LOG_UNIMP, "%s: Failed to write the host device..\n", __func__); ++ return; ++ } ++ ++ memcpy(&s->mem[RET_COD], &messg.rx.ret, sizeof(messg.rx.ret)); ++ memcpy(&s->mem[RX_SIZ], &messg.rx.size, sizeof(messg.rx.size)); ++ ++ break; ++ ++ default: ++ ++ memcpy(&s->mem[addr], &data, size); ++ } ++ ++ return; ++} ++ ++static const MemoryRegionOps nvidia_bpmp_guest_ops = { ++ .read = nvidia_bpmp_guest_read, ++ .write = nvidia_bpmp_guest_write, ++ .endianness = DEVICE_NATIVE_ENDIAN, ++}; ++ ++static void nvidia_bpmp_guest_instance_init(Object *obj) ++{ ++ NvidiaBpmpGuestState *s = NVIDIA_BPMP_GUEST(obj); ++ ++ /* allocate memory map region */ ++ memory_region_init_io(&s->iomem, obj, &nvidia_bpmp_guest_ops, s, TYPE_NVIDIA_BPMP_GUEST, MEM_SIZE); ++ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); ++ ++ s->host_device_fd = open(HOST_DEVICE_PATH, O_RDWR); // Open the device with read/write access ++ ++ if (s->host_device_fd < 0) ++ { ++ qemu_log_mask(LOG_UNIMP, "%s: Failed to open the host device..\n", __func__); ++ return; ++ } ++} ++ ++/* create a new type to define the info related to our device */ ++static const TypeInfo nvidia_bpmp_guest_info = { ++ .name = TYPE_NVIDIA_BPMP_GUEST, ++ .parent = TYPE_SYS_BUS_DEVICE, ++ .instance_size = sizeof(NvidiaBpmpGuestState), ++ .instance_init = nvidia_bpmp_guest_instance_init, ++}; ++ ++static void nvidia_bpmp_guest_register_types(void) ++{ ++ type_register_static(&nvidia_bpmp_guest_info); ++} ++ ++type_init(nvidia_bpmp_guest_register_types) ++ ++ /* ++ * Create the Nvidia BPMP guest device. ++ */ ++ DeviceState *nvidia_bpmp_guest_create(hwaddr addr) ++{ ++ DeviceState *dev = qdev_new(TYPE_NVIDIA_BPMP_GUEST); ++ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); ++ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); ++ return dev; ++} +diff --git a/hw/misc/nvidia_bpmp_guest.h b/hw/misc/nvidia_bpmp_guest.h +new file mode 100644 +index 0000000000..dd4b4221d3 +--- /dev/null ++++ b/hw/misc/nvidia_bpmp_guest.h +@@ -0,0 +1,9 @@ ++#ifndef HW_NVIDIA_BPMP_GUEST_H ++#define HW_NVIDIA_BPMP_GUEST_H ++ ++#include "qom/object.h" ++ ++DeviceState *nvidia_bpmp_guest_create(hwaddr); ++ ++ ++#endif +diff --git a/hw/misc/nvidia_gpio_guest.c b/hw/misc/nvidia_gpio_guest.c +new file mode 100644 +index 0000000000..4bd0283b00 +--- /dev/null ++++ b/hw/misc/nvidia_gpio_guest.c +@@ -0,0 +1,254 @@ ++#include "qemu/osdep.h" ++#include "qemu/qemu-print.h" ++#include "qemu/log.h" ++#include "qapi/error.h" /* provides error_fatal() handler */ ++#include "hw/sysbus.h" /* provides all sysbus registering func */ ++#include "hw/misc/nvidia_gpio_guest.h" ++ ++#define TYPE_NVIDIA_GPIO_GUEST "nvidia_gpio_guest" ++typedef struct NvidiaGpioGuestState NvidiaGpioGuestState; ++DECLARE_INSTANCE_CHECKER(NvidiaGpioGuestState, NVIDIA_GPIO_GUEST, TYPE_NVIDIA_GPIO_GUEST) ++ ++#define MEM_SIZE 0x18 // mem size in bytes is 3 64 bit words ++#define RETURN_OFF 0x10 // offset (in bytes) for return value is two 64 bit words ++#define RETURN_SIZE 8 ++// #define RETURN_OFF 0 ++#define HOST_DEVICE_PATH "/dev/gpio-host" ++ ++#define GPIO_PT_DEBUG ++// #define GPIO_PT_DEBUG_VERBOSE ++ ++_Static_assert(sizeof(uint64_t) == RETURN_SIZE, "size assertion for RETURN_SIZE failed"); ++_Static_assert(sizeof(uint64_t)*3 == MEM_SIZE, "size assertion for MEM_SIZE failed"); ++_Static_assert(sizeof(uint64_t)*2 == RETURN_OFF, "size assertion for RETURN_OFF failed"); ++ ++ ++struct NvidiaGpioGuestState ++{ ++ SysBusDevice parent_obj; ++ MemoryRegion iomem; ++ int host_device_fd; ++ uint8_t mem[MEM_SIZE]; ++ unsigned char length; ++ int towrite, written; ++ uint64_t return_value; ++}; ++ ++pthread_mutex_t io_mutex = PTHREAD_MUTEX_INITIALIZER; ++pthread_mutex_t return_mutex = PTHREAD_MUTEX_INITIALIZER; ++ ++// Device memory: 0x090c1000 + /* Base address */ ++ ++static uint64_t nvidia_gpio_guest_read(void *opaque, hwaddr addr, unsigned int size) ++{ ++ struct NvidiaGpioGuestState *s = opaque; ++ uint64_t mask = ( size >= 8) ? (uint64_t)0xFFFFFFFFFFFFFFFF : ( (uint64_t)0x0000000000000001 << (size << 3) ) - 1; ++ uint64_t retval = ( s->return_value >> (addr<<3) ) & mask; ++ ++ #ifdef GPIO_PT_DEBUG_VERBOSE ++ qemu_printf("qemu: ( + read ) written: %d: addr: %ld, size: %d, return_value: 0x%016lX, retval: 0x%lX\n", s->written, addr, size, s->return_value, retval); ++ #endif ++ ++ s->written -= size; ++ ++ if ( s->written < 0 ) { ++ qemu_printf("%s: **Error** Size error in read (%d)\n", __func__, s->written); ++ s->written = 0; // allow next message ++ retval = 0x01234567ABEDFACE; ++ } ++ ++ if( size + addr > RETURN_OFF + RETURN_SIZE || size > RETURN_SIZE ) { ++ qemu_printf("%s: **Error** address or size overflow, addr: 0x%lX, size: %d\n", __func__, addr, size); ++ s->written = 0; // allow next message ++ retval = 0x01234567ABEDFACE; ++ } ++ ++ if ( s->written == 0 ) { ++ pthread_mutex_unlock(&return_mutex); // allow next message ++ } ++ ++ #ifdef GPIO_PT_DEBUG_VERBOSE ++ qemu_printf("qemu: ( read ) retval(processed): 0x%lX\n", retval); ++ #endif ++ return retval; ++} ++ ++/* ++ * void *opaque: This is a pointer to opaque data associated with the memory region. ++ * It is used to pass additional context or information to the callback function. ++ * In this case, it can be used to access device-specific data structures or state ++ * information required for handling the write operation. ++ * ++ * hwaddr addr: This parameter represents the hardware address or the offset within the memory region ++ * where the write operation is taking place. It specifies the location where the data should be written. ++ * ++ * uint64_t data: This parameter holds the data that needs to be written to the memory region. ++ * It represents the value that will be stored at the specified address (addr). ++ * ++ * unsigned int size: This parameter indicates the size of the data being written, in bytes. It specifies the number of bytes ++ * to be written starting from the given address. ++ */ ++ ++static inline ssize_t safe_write(int fd, const void *buf, size_t count) { ++ ssize_t ret; ++ pthread_mutex_lock(&io_mutex); ++ ret = write(fd, buf, count); ++ pthread_mutex_unlock(&io_mutex); ++ return ret; ++} ++ ++static void nvidia_gpio_guest_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) ++{ ++ NvidiaGpioGuestState *s = opaque; ++ int ret = 0; ++ uint64_t mask; ++ #ifdef GPIO_PT_DEBUG_VERBOSE ++ int i; ++ #endif ++ ++ if(addr == 0) { ++ s->length = (*(unsigned char *)&data & 0xFE) >> 1; // s->length is 7 top MSB bits in first byte ++ *(unsigned char *)&data = *(unsigned char *)&data & 0x01; // remove lenght data from message ++ memset(s->mem, 0, s->length); ++ s->towrite = 0; ++ #ifdef GPIO_PT_DEBUG_VERBOSE ++ // print debug ++ qemu_printf("qemu: ( +++ write +++ ) length (coded in msg): %d\n", s->length); ++ #endif ++ } ++ ++ #ifdef GPIO_PT_DEBUG_VERBOSE ++ qemu_printf("qemu: ( write ) addr: %ld, size: %d, data: 0x%016lX\n", addr, size, data); ++ #endif ++ ++ if (addr > s->length - size){ ++ qemu_printf("%s: **Error** addr (%ld) > s->length (%d)- size (%d)\n", __func__, addr, s->length, size); ++ return; ++ } ++ ++ // accumulate message ++ memcpy(s->mem + addr, &data, size); ++ s->towrite += size; ++ ++ // writeing last block ++ if(addr == s->length - size) { ++ // print debug ++ #ifdef GPIO_PT_DEBUG_VERBOSE ++ qemu_printf("qemu: ( write ) signal \'%c\', hexdump:\n", s->mem[1]); ++ for(i = 0; i < (s->towrite + 7)/8; i++) ++ qemu_printf("\t\t\t\t(%d) 0x%016lX\n", i, *((uint64_t *)(s->mem+i))); ++ #endif ++ ++ if( s->length > 0x18 || s->mem[0]&0xFE || s->mem[1] >= 0x80 || s->mem[1] < 0x20) { // block obvious errors only ++ s->return_value = 0xDEAFFACE; ++ qemu_printf("%s: **Error** signal \'%c\' was blocked (chip=%d)\n", __func__, s->mem[1], s->mem[0]); ++ } ++ else { ++ ++ if( s->towrite != s->length ) { // size check before write ++ qemu_printf("%s: **Error** Size error in write %d of %d\n", __func__, s->towrite, s->length); ++ return; ++ } ++ ++ pthread_mutex_lock(&return_mutex); ++ #ifdef GPIO_PT_DEBUG_VERBOSE ++ qemu_printf("qemu: ( write ) +++locked+++ return mutex\n"); ++ qemu_printf("qemu: ( write ) Ready to write, (%d)\n", s->towrite); ++ #endif ++ if ( (ret = safe_write(s->host_device_fd, s->mem, s->towrite)) < 0 ) ++ { ++ // error in write() ++ qemu_printf("%s: **Error** 0x%02X, Failed to write the host device (%d)\n", __func__, errno, s->towrite); ++ s->return_value = 0x1BADFACE; ++ s->written = 0; ++ pthread_mutex_unlock(&return_mutex); // allow next message ++ } ++ else { ++ #ifdef GPIO_PT_DEBUG ++ qemu_printf("qemu: **Success** writing (%d) signal \'%c\' to the host device\n", ret, s->mem[1]); ++ #endif ++ // in addition to written length, 'ret' contains also the count of returned bytes appended to the end of the buffer ++ // note: the size of the return value is (s->written - RETURN_OFF), should be 4 or 8 bytes ++ s->written = ret; // update 'written' to actually written bytes ('ret'|| may be altered by host's return value) ++ s->written -= RETURN_OFF; // begin to handle return, subtract return offset to get expected return size ++ if ( s->written > 0 && s->written <= RETURN_SIZE ) { ++ // a return value is available ++ #ifdef GPIO_PT_DEBUG_VERBOSE ++ qemu_printf("qemu: ( write ) Expected return size: %d\n", s->written); ++ #endif ++ // note: shift left does not work when we shift 64 bits (8 bytes) because the '1' is lost at 65 bits ++ mask = ( s->written >= 8) ? (uint64_t)0xFFFFFFFFFFFFFFFF : ( (uint64_t)0x0000000000000001 << (s->written << 3) ) - 1; ++ s->return_value = *(uint64_t *)(s->mem + RETURN_OFF) & mask; ++ #ifdef GPIO_PT_DEBUG_VERBOSE ++ qemu_printf("qemu: ( write ) Return value 0x%016lX, is copied from raw 0x%016lX, with mask = 0x%016lX\n", s->return_value, *(uint64_t *)(s->mem + RETURN_OFF), mask); ++ #endif ++ } ++ else { ++ // no return value ++ s->written = 0; ++ if ( ret != s->length ) { // size check after write at least 'length' must be written (return padding may occur) ++ qemu_printf("%s: **Warning** %d bytes of %d, were written to host\n", __func__, s->written, s->length); ++ s->return_value = 0x2BADFACE; ++ } ++ pthread_mutex_unlock(&return_mutex); // allow next message ++ } ++ } ++ ++ } // close error check ++ ++ #ifdef GPIO_PT_DEBUG ++ qemu_printf("qemu: ( write --- ) return_value: 0x%016lX\n", s->return_value); ++ #endif ++ } ++ return; ++} ++ ++static const MemoryRegionOps nvidia_gpio_guest_ops = { ++ .read = nvidia_gpio_guest_read, ++ .write = nvidia_gpio_guest_write, ++ .endianness = DEVICE_NATIVE_ENDIAN, ++}; ++ ++static void nvidia_gpio_guest_instance_init(Object *obj) ++{ ++ struct NvidiaGpioGuestState *s = NVIDIA_GPIO_GUEST(obj); ++ memset(s->mem, 0, MEM_SIZE); ++ ++ /* allocate memory map region */ ++ memory_region_init_io(&s->iomem, obj, &nvidia_gpio_guest_ops, s, TYPE_NVIDIA_GPIO_GUEST, MEM_SIZE); ++ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); ++ ++ s->host_device_fd = open(HOST_DEVICE_PATH, O_RDWR); // Open the device with read/write access ++ ++ if (s->host_device_fd < 0) ++ { ++ qemu_printf("%s: **Error** Failed to open the host device..\n", __func__); ++ return; ++ } ++} ++ ++/* create a new type to define the info related to our device */ ++static const TypeInfo nvidia_gpio_guest_info = { ++ .name = TYPE_NVIDIA_GPIO_GUEST, ++ .parent = TYPE_SYS_BUS_DEVICE, ++ .instance_size = sizeof(NvidiaGpioGuestState), ++ .instance_init = nvidia_gpio_guest_instance_init, ++}; ++ ++static void nvidia_gpio_guest_register_types(void) ++{ ++ type_register_static(&nvidia_gpio_guest_info); ++} ++ ++type_init(nvidia_gpio_guest_register_types) ++ ++ /* ++ * Create the Nvidia GPIO guest device. ++ */ ++ DeviceState *nvidia_gpio_guest_create(hwaddr addr) ++{ ++ DeviceState *dev = qdev_new(TYPE_NVIDIA_GPIO_GUEST); ++ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); ++ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); ++ return dev; ++} +diff --git a/hw/misc/nvidia_gpio_guest.h b/hw/misc/nvidia_gpio_guest.h +new file mode 100644 +index 0000000000..9e1a693fb2 +--- /dev/null ++++ b/hw/misc/nvidia_gpio_guest.h +@@ -0,0 +1,9 @@ ++#ifndef HW_NVIDIA_GPIO_GUEST_H ++#define HW_NVIDIA_GPIO_GUEST_H ++ ++#include "qom/object.h" ++ ++DeviceState *nvidia_gpio_guest_create(hwaddr); ++ ++ ++#endif +diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h +index bb486d36b1..b5ad45d3a3 100644 +--- a/include/hw/arm/virt.h ++++ b/include/hw/arm/virt.h +@@ -67,6 +67,8 @@ enum { + VIRT_PCIE_MMIO, + VIRT_PCIE_PIO, + VIRT_PCIE_ECAM, ++ VIRT_NVIDIA_BPMP_GUEST, ++ VIRT_NVIDIA_GPIO_GUEST, + VIRT_PLATFORM_BUS, + VIRT_GPIO, + VIRT_SECURE_UART, +diff --git a/mk_patch.sh b/mk_patch.sh +new file mode 100755 +index 0000000000..bd11d732d0 +--- /dev/null ++++ b/mk_patch.sh +@@ -0,0 +1 @@ ++git diff origin/stable-9.0 > qemu-passthrough.patch +diff --git a/qemu-passthrough.nix b/qemu-passthrough.nix +new file mode 100644 +index 0000000000..71135a4d19 +--- /dev/null ++++ b/qemu-passthrough.nix +@@ -0,0 +1,143 @@ ++# qemu-passthrough.nix ++# build with command: nix-build qemu-passthrough.nix ++{ pkgs ? import {}, ... }: ++ ++pkgs.stdenv.mkDerivation rec { ++ pname = "qemu-passthrough"; ++ version = "1.1"; ++ ++ src = builtins.path { ++ path = ~/software/Jetson/Linux_for_Tegra/sources/kernel/qemu-passthrough; ++ name = "qemu-source"; ++ }; ++ ++ buildInputs = with pkgs; [ ++ util-linux ++ pkg-config ++ autogen ++ automake ++ flex ++ bison ++ meson ++ ninja ++ cmake ++ json_c ++ gcc ++ gnumake ++ libtool ++ valgrind ++ python3 ++ glib ++ dbus ++ pixman ++ zlib ++ bzip2 ++ lzo ++ libgpiod ++ snappy ++ curl ++ libssh ++ libcap ++ libepoxy ++ nettle ++ attr ++ systemd ++ liburing ++ makeWrapper ++ mktemp ++ libdrm ++ wayland-protocols ++ sphinx ++ python311Packages.sphinx-rtd-theme ++ SDL2 ++ gtk3 ++ gnutls ++ libslirp ++ libselinux ++ alsa-lib ++ alsa-oss ++ pulseaudio ++ pipewire ++ acpica-tools ++ pam_p11 ++ pam_u2f ++ vte ++ libibumad ++ libnfs ++ libseccomp ++ libxkbcommon ++ libcacard ++ libusb1 ++ libaio ++ libcap_ng ++ libtasn1 ++ libgcrypt ++ keyutils ++ canokey-qemu ++ fuse3 ++ libbpf ++ capstone ++ fdtools ++ vde2 ++ texinfo ++ spice ++ virglrenderer ++ multipath-tools ++ ncurses ++ sealcurses ++ lzfse ++ gsasl ++ xgboost ++ libvncserver ++ cmocka ++ basez ++ SDL2 ++ SDL2_image ++ ceph ++ gsasl ++ xdp-tools ++ fdtools ++ dtc ++ ]; ++ ++ configurePhase = '' ++ ./configure --target-list=aarch64-softmmu \ ++ --enable-sdl --enable-gtk --enable-opengl \ ++ --disable-dbus-display \ ++ --enable-vnc --enable-vnc-jpeg \ ++ --disable-docs \ ++ --prefix=$out \ ++ --enable-vde \ ++ --enable-vhost-net --enable-vhost-user \ ++ ''; ++ /* ++ --enable-vhost-kernel --enable-vhost-net --enable-vhost-user \ ++ --enable-vhost-user-blk-server --enable-vfio-user-server \ ++ --enable-vhost-crypto --enable-vhost-vdpa --enable-virtfs \ ++ --enable-virtfs-proxy-helper \ ++ --sysconfdir=${lib}$(if isDebug then "/debug" else "") \ ++ --disable-werror \ ++ --without-examples \ ++ --docdir=${share}/doc/${pname}-${version}; ++ */ ++ ++ ++ buildPhase = '' ++ make -j12 ++ ''; ++ ++ installPhase = '' ++ make install && \ ++ [ -x $out/bin/qemu-system-aarch64 ] && \ ++ ln -s $out/bin/qemu-system-aarch64 $out/bin/qemu-passthrough; ++ ''; ++ ++ meta = with pkgs.lib; { ++ description = "QEMU with passthrough modifications for Ghaf"; ++ homepage = "https://github.com/KimGSandstrom/qemu-passthrough"; ++ license = licenses.gpl2Plus; ++ maintainers = with maintainers; [ KimGSandstrom ]; ++ platforms = platforms.linux; ++ }; ++} ++ +diff --git a/qemu-passthrough.patch b/qemu-passthrough.patch +new file mode 100644 +index 0000000000..e1dc27944f +--- /dev/null ++++ b/qemu-passthrough.patch +@@ -0,0 +1,672 @@ ++diff --git a/configs/devices/aarch64-softmmu/default.mak b/configs/devices/aarch64-softmmu/default.mak ++index f82a04c27d..35dd533d37 100644 ++--- a/configs/devices/aarch64-softmmu/default.mak +++++ b/configs/devices/aarch64-softmmu/default.mak ++@@ -8,3 +8,6 @@ include ../arm-softmmu/default.mak ++ # CONFIG_XLNX_ZYNQMP_ARM=n ++ # CONFIG_XLNX_VERSAL=n ++ # CONFIG_SBSA_REF=n +++ +++CONFIG_NVIDIA_BPMP_GUEST=y +++CONFIG_NVIDIA_GPIO_GUEST=y ++diff --git a/hw/arm/virt.c b/hw/arm/virt.c ++index a9a913aead..4d79d3d5ec 100644 ++--- a/hw/arm/virt.c +++++ b/hw/arm/virt.c ++@@ -83,6 +83,8 @@ ++ #include "hw/virtio/virtio-md-pci.h" ++ #include "hw/virtio/virtio-iommu.h" ++ #include "hw/char/pl011.h" +++#include "hw/misc/nvidia_bpmp_guest.h" +++#include "hw/misc/nvidia_gpio_guest.h" ++ #include "qemu/guest-random.h" ++ ++ static GlobalProperty arm_virt_compat[] = { ++@@ -176,6 +178,8 @@ static const MemMapEntry base_memmap[] = { ++ [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, ++ [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, ++ [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, +++ [VIRT_NVIDIA_BPMP_GUEST] = { 0x090c0000, 0x00001000 }, +++ [VIRT_NVIDIA_GPIO_GUEST] = { 0x090c1000, 0x00001000 }, ++ [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, ++ /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ ++ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, ++@@ -1072,6 +1076,12 @@ static void create_virtio_devices(const VirtMachineState *vms) ++ hwaddr size = vms->memmap[VIRT_MMIO].size; ++ MachineState *ms = MACHINE(vms); ++ +++ /* Create NVIDIA BPMP guest passthru device, possibly need update ftd - WIP */ +++ nvidia_bpmp_guest_create(vms->memmap[VIRT_NVIDIA_BPMP_GUEST].base); +++ +++ /* Create NVIDIA GPIO guest passthru device, possibly need update ftd - WIP */ +++ nvidia_gpio_guest_create(vms->memmap[VIRT_NVIDIA_GPIO_GUEST].base); +++ ++ /* We create the transports in forwards order. Since qbus_realize() ++ * prepends (not appends) new child buses, the incrementing loop below will ++ * create a list of virtio-mmio buses with decreasing base addresses. ++diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig ++index 1e08785b83..0f762a0119 100644 ++--- a/hw/misc/Kconfig +++++ b/hw/misc/Kconfig ++@@ -11,6 +11,12 @@ config ARMSSE_MHU ++ config ARMSSE_CPU_PWRCTRL ++ bool ++ +++config NVIDIA_BPMP_GUEST +++ bool +++ +++config NVIDIA_GPIO_GUEST +++ bool +++ ++ config ISA_DEBUG ++ bool ++ depends on ISA_BUS ++diff --git a/hw/misc/meson.build b/hw/misc/meson.build ++index 86596a3888..876417cdec 100644 ++--- a/hw/misc/meson.build +++++ b/hw/misc/meson.build ++@@ -1,4 +1,6 @@ ++ system_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c')) +++system_ss.add(when: 'CONFIG_NVIDIA_BPMP_GUEST', if_true: files('nvidia_bpmp_guest.c')) +++system_ss.add(when: 'CONFIG_NVIDIA_GPIO_GUEST', if_true: files('nvidia_gpio_guest.c')) ++ system_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) ++ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) ++ system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) ++diff --git a/hw/misc/nvidia_bpmp_guest.c b/hw/misc/nvidia_bpmp_guest.c ++new file mode 100644 ++index 0000000000..3facee6d00 ++--- /dev/null +++++ b/hw/misc/nvidia_bpmp_guest.c ++@@ -0,0 +1,171 @@ +++#include "qemu/osdep.h" +++#include "qemu/log.h" +++#include "qapi/error.h" /* provides error_fatal() handler */ +++#include "hw/sysbus.h" /* provides all sysbus registering func */ +++#include "hw/misc/nvidia_bpmp_guest.h" +++ +++#define TYPE_NVIDIA_BPMP_GUEST "nvidia_bpmp_guest" +++typedef struct NvidiaBpmpGuestState NvidiaBpmpGuestState; +++DECLARE_INSTANCE_CHECKER(NvidiaBpmpGuestState, NVIDIA_BPMP_GUEST, TYPE_NVIDIA_BPMP_GUEST) +++ +++#define TX_BUF 0x0000 +++#define RX_BUF 0x0200 +++#define TX_SIZ 0x0400 +++#define RX_SIZ 0x0408 +++#define RET_COD 0x0410 +++#define MRQ 0x0500 +++ +++#define MEM_SIZE 0x600 +++#define HOST_DEVICE_PATH "/dev/bpmp-host" +++#define MESSAGE_SIZE 0x0200 +++ +++// qemu_log_mask(LOG_UNIMP, "%s: \n", __func__ ); +++ +++struct NvidiaBpmpGuestState +++{ +++ SysBusDevice parent_obj; +++ MemoryRegion iomem; +++ int host_device_fd; +++ uint8_t mem[MEM_SIZE]; +++}; +++ +++// Device memory map: +++ +++// 0x090c0000 + /* Base address, size 0x01000 */ +++ +++// 0x0000 \ Tx buffer +++// 0x01FF / +++// 0x0200 \ Rx buffer +++// 0x03FF / +++// 0x0400 -- Tx size +++// 0x0408 -- Rx size +++// 0x0410 -- Ret +++// 0x0500 -- mrq +++ +++ +++ +++// Data should be aligned to 64bit paragraph. +++ +++// Protocol is: +++// 1. Write data buffers to 0x0000-0x01FF and 0x0200-0x03FF +++// 2. Write buffer sizes to 0x0400 (Tx) and 0x0408 (Rx) +++// 2. Start operation by writing mrq opcode to address 0x0500 +++// 3. Read ret code from 0x0410 and response data from the buffers +++ +++static uint64_t nvidia_bpmp_guest_read(void *opaque, hwaddr addr, unsigned int size) +++{ +++ NvidiaBpmpGuestState *s = opaque; +++ +++ if (addr >= MEM_SIZE) +++ return 0xDEADBEEF; +++ +++ // Cast buffer location as uint64_t +++ return *(uint64_t*)&s->mem[addr]; +++} +++ +++static void nvidia_bpmp_guest_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) +++{ +++ NvidiaBpmpGuestState *s = opaque; +++ int ret; +++ +++ struct +++ { +++ unsigned int mrq; +++ struct +++ { +++ void *data; +++ size_t size; +++ } tx; +++ struct +++ { +++ void *data; +++ size_t size; +++ int ret; +++ } rx; +++ } messg; +++ +++ memset(&messg, 0, sizeof(messg)); +++ +++ if (addr >= MEM_SIZE){ +++ qemu_log_mask(LOG_UNIMP, "qemu: Error addr >= MEM_SIZE in 0x%lX data: 0x%lX\n", addr, data); +++ return; +++ } +++ +++ switch (addr) +++ { +++ case MRQ: +++ // set up the structure +++ messg.mrq = data; +++ messg.tx.data = &s->mem[TX_BUF]; +++ memcpy(&messg.tx.size, &s->mem[TX_SIZ], sizeof(messg.tx.size)); +++ messg.rx.data = &s->mem[RX_BUF]; +++ memcpy(&messg.rx.size, &s->mem[RX_SIZ], sizeof(messg.rx.size)); +++ +++ ret = write(s->host_device_fd, &messg, sizeof(messg)); // Send the data to the host module +++ if (ret < 0) +++ { +++ qemu_log_mask(LOG_UNIMP, "%s: Failed to write the host device..\n", __func__); +++ return; +++ } +++ +++ memcpy(&s->mem[RET_COD], &messg.rx.ret, sizeof(messg.rx.ret)); +++ memcpy(&s->mem[RX_SIZ], &messg.rx.size, sizeof(messg.rx.size)); +++ +++ break; +++ +++ default: +++ +++ memcpy(&s->mem[addr], &data, size); +++ } +++ +++ return; +++} +++ +++static const MemoryRegionOps nvidia_bpmp_guest_ops = { +++ .read = nvidia_bpmp_guest_read, +++ .write = nvidia_bpmp_guest_write, +++ .endianness = DEVICE_NATIVE_ENDIAN, +++}; +++ +++static void nvidia_bpmp_guest_instance_init(Object *obj) +++{ +++ NvidiaBpmpGuestState *s = NVIDIA_BPMP_GUEST(obj); +++ +++ /* allocate memory map region */ +++ memory_region_init_io(&s->iomem, obj, &nvidia_bpmp_guest_ops, s, TYPE_NVIDIA_BPMP_GUEST, MEM_SIZE); +++ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); +++ +++ s->host_device_fd = open(HOST_DEVICE_PATH, O_RDWR); // Open the device with read/write access +++ +++ if (s->host_device_fd < 0) +++ { +++ qemu_log_mask(LOG_UNIMP, "%s: Failed to open the host device..\n", __func__); +++ return; +++ } +++} +++ +++/* create a new type to define the info related to our device */ +++static const TypeInfo nvidia_bpmp_guest_info = { +++ .name = TYPE_NVIDIA_BPMP_GUEST, +++ .parent = TYPE_SYS_BUS_DEVICE, +++ .instance_size = sizeof(NvidiaBpmpGuestState), +++ .instance_init = nvidia_bpmp_guest_instance_init, +++}; +++ +++static void nvidia_bpmp_guest_register_types(void) +++{ +++ type_register_static(&nvidia_bpmp_guest_info); +++} +++ +++type_init(nvidia_bpmp_guest_register_types) +++ +++ /* +++ * Create the Nvidia BPMP guest device. +++ */ +++ DeviceState *nvidia_bpmp_guest_create(hwaddr addr) +++{ +++ DeviceState *dev = qdev_new(TYPE_NVIDIA_BPMP_GUEST); +++ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); +++ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); +++ return dev; +++} ++diff --git a/hw/misc/nvidia_bpmp_guest.h b/hw/misc/nvidia_bpmp_guest.h ++new file mode 100644 ++index 0000000000..dd4b4221d3 ++--- /dev/null +++++ b/hw/misc/nvidia_bpmp_guest.h ++@@ -0,0 +1,9 @@ +++#ifndef HW_NVIDIA_BPMP_GUEST_H +++#define HW_NVIDIA_BPMP_GUEST_H +++ +++#include "qom/object.h" +++ +++DeviceState *nvidia_bpmp_guest_create(hwaddr); +++ +++ +++#endif ++diff --git a/hw/misc/nvidia_gpio_guest.c b/hw/misc/nvidia_gpio_guest.c ++new file mode 100644 ++index 0000000000..4bd0283b00 ++--- /dev/null +++++ b/hw/misc/nvidia_gpio_guest.c ++@@ -0,0 +1,254 @@ +++#include "qemu/osdep.h" +++#include "qemu/qemu-print.h" +++#include "qemu/log.h" +++#include "qapi/error.h" /* provides error_fatal() handler */ +++#include "hw/sysbus.h" /* provides all sysbus registering func */ +++#include "hw/misc/nvidia_gpio_guest.h" +++ +++#define TYPE_NVIDIA_GPIO_GUEST "nvidia_gpio_guest" +++typedef struct NvidiaGpioGuestState NvidiaGpioGuestState; +++DECLARE_INSTANCE_CHECKER(NvidiaGpioGuestState, NVIDIA_GPIO_GUEST, TYPE_NVIDIA_GPIO_GUEST) +++ +++#define MEM_SIZE 0x18 // mem size in bytes is 3 64 bit words +++#define RETURN_OFF 0x10 // offset (in bytes) for return value is two 64 bit words +++#define RETURN_SIZE 8 +++// #define RETURN_OFF 0 +++#define HOST_DEVICE_PATH "/dev/gpio-host" +++ +++#define GPIO_PT_DEBUG +++// #define GPIO_PT_DEBUG_VERBOSE +++ +++_Static_assert(sizeof(uint64_t) == RETURN_SIZE, "size assertion for RETURN_SIZE failed"); +++_Static_assert(sizeof(uint64_t)*3 == MEM_SIZE, "size assertion for MEM_SIZE failed"); +++_Static_assert(sizeof(uint64_t)*2 == RETURN_OFF, "size assertion for RETURN_OFF failed"); +++ +++ +++struct NvidiaGpioGuestState +++{ +++ SysBusDevice parent_obj; +++ MemoryRegion iomem; +++ int host_device_fd; +++ uint8_t mem[MEM_SIZE]; +++ unsigned char length; +++ int towrite, written; +++ uint64_t return_value; +++}; +++ +++pthread_mutex_t io_mutex = PTHREAD_MUTEX_INITIALIZER; +++pthread_mutex_t return_mutex = PTHREAD_MUTEX_INITIALIZER; +++ +++// Device memory: 0x090c1000 + /* Base address */ +++ +++static uint64_t nvidia_gpio_guest_read(void *opaque, hwaddr addr, unsigned int size) +++{ +++ struct NvidiaGpioGuestState *s = opaque; +++ uint64_t mask = ( size >= 8) ? (uint64_t)0xFFFFFFFFFFFFFFFF : ( (uint64_t)0x0000000000000001 << (size << 3) ) - 1; +++ uint64_t retval = ( s->return_value >> (addr<<3) ) & mask; +++ +++ #ifdef GPIO_PT_DEBUG_VERBOSE +++ qemu_printf("qemu: ( + read ) written: %d: addr: %ld, size: %d, return_value: 0x%016lX, retval: 0x%lX\n", s->written, addr, size, s->return_value, retval); +++ #endif +++ +++ s->written -= size; +++ +++ if ( s->written < 0 ) { +++ qemu_printf("%s: **Error** Size error in read (%d)\n", __func__, s->written); +++ s->written = 0; // allow next message +++ retval = 0x01234567ABEDFACE; +++ } +++ +++ if( size + addr > RETURN_OFF + RETURN_SIZE || size > RETURN_SIZE ) { +++ qemu_printf("%s: **Error** address or size overflow, addr: 0x%lX, size: %d\n", __func__, addr, size); +++ s->written = 0; // allow next message +++ retval = 0x01234567ABEDFACE; +++ } +++ +++ if ( s->written == 0 ) { +++ pthread_mutex_unlock(&return_mutex); // allow next message +++ } +++ +++ #ifdef GPIO_PT_DEBUG_VERBOSE +++ qemu_printf("qemu: ( read ) retval(processed): 0x%lX\n", retval); +++ #endif +++ return retval; +++} +++ +++/* +++ * void *opaque: This is a pointer to opaque data associated with the memory region. +++ * It is used to pass additional context or information to the callback function. +++ * In this case, it can be used to access device-specific data structures or state +++ * information required for handling the write operation. +++ * +++ * hwaddr addr: This parameter represents the hardware address or the offset within the memory region +++ * where the write operation is taking place. It specifies the location where the data should be written. +++ * +++ * uint64_t data: This parameter holds the data that needs to be written to the memory region. +++ * It represents the value that will be stored at the specified address (addr). +++ * +++ * unsigned int size: This parameter indicates the size of the data being written, in bytes. It specifies the number of bytes +++ * to be written starting from the given address. +++ */ +++ +++static inline ssize_t safe_write(int fd, const void *buf, size_t count) { +++ ssize_t ret; +++ pthread_mutex_lock(&io_mutex); +++ ret = write(fd, buf, count); +++ pthread_mutex_unlock(&io_mutex); +++ return ret; +++} +++ +++static void nvidia_gpio_guest_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) +++{ +++ NvidiaGpioGuestState *s = opaque; +++ int ret = 0; +++ uint64_t mask; +++ #ifdef GPIO_PT_DEBUG_VERBOSE +++ int i; +++ #endif +++ +++ if(addr == 0) { +++ s->length = (*(unsigned char *)&data & 0xFE) >> 1; // s->length is 7 top MSB bits in first byte +++ *(unsigned char *)&data = *(unsigned char *)&data & 0x01; // remove lenght data from message +++ memset(s->mem, 0, s->length); +++ s->towrite = 0; +++ #ifdef GPIO_PT_DEBUG_VERBOSE +++ // print debug +++ qemu_printf("qemu: ( +++ write +++ ) length (coded in msg): %d\n", s->length); +++ #endif +++ } +++ +++ #ifdef GPIO_PT_DEBUG_VERBOSE +++ qemu_printf("qemu: ( write ) addr: %ld, size: %d, data: 0x%016lX\n", addr, size, data); +++ #endif +++ +++ if (addr > s->length - size){ +++ qemu_printf("%s: **Error** addr (%ld) > s->length (%d)- size (%d)\n", __func__, addr, s->length, size); +++ return; +++ } +++ +++ // accumulate message +++ memcpy(s->mem + addr, &data, size); +++ s->towrite += size; +++ +++ // writeing last block +++ if(addr == s->length - size) { +++ // print debug +++ #ifdef GPIO_PT_DEBUG_VERBOSE +++ qemu_printf("qemu: ( write ) signal \'%c\', hexdump:\n", s->mem[1]); +++ for(i = 0; i < (s->towrite + 7)/8; i++) +++ qemu_printf("\t\t\t\t(%d) 0x%016lX\n", i, *((uint64_t *)(s->mem+i))); +++ #endif +++ +++ if( s->length > 0x18 || s->mem[0]&0xFE || s->mem[1] >= 0x80 || s->mem[1] < 0x20) { // block obvious errors only +++ s->return_value = 0xDEAFFACE; +++ qemu_printf("%s: **Error** signal \'%c\' was blocked (chip=%d)\n", __func__, s->mem[1], s->mem[0]); +++ } +++ else { +++ +++ if( s->towrite != s->length ) { // size check before write +++ qemu_printf("%s: **Error** Size error in write %d of %d\n", __func__, s->towrite, s->length); +++ return; +++ } +++ +++ pthread_mutex_lock(&return_mutex); +++ #ifdef GPIO_PT_DEBUG_VERBOSE +++ qemu_printf("qemu: ( write ) +++locked+++ return mutex\n"); +++ qemu_printf("qemu: ( write ) Ready to write, (%d)\n", s->towrite); +++ #endif +++ if ( (ret = safe_write(s->host_device_fd, s->mem, s->towrite)) < 0 ) +++ { +++ // error in write() +++ qemu_printf("%s: **Error** 0x%02X, Failed to write the host device (%d)\n", __func__, errno, s->towrite); +++ s->return_value = 0x1BADFACE; +++ s->written = 0; +++ pthread_mutex_unlock(&return_mutex); // allow next message +++ } +++ else { +++ #ifdef GPIO_PT_DEBUG +++ qemu_printf("qemu: **Success** writing (%d) signal \'%c\' to the host device\n", ret, s->mem[1]); +++ #endif +++ // in addition to written length, 'ret' contains also the count of returned bytes appended to the end of the buffer +++ // note: the size of the return value is (s->written - RETURN_OFF), should be 4 or 8 bytes +++ s->written = ret; // update 'written' to actually written bytes ('ret'|| may be altered by host's return value) +++ s->written -= RETURN_OFF; // begin to handle return, subtract return offset to get expected return size +++ if ( s->written > 0 && s->written <= RETURN_SIZE ) { +++ // a return value is available +++ #ifdef GPIO_PT_DEBUG_VERBOSE +++ qemu_printf("qemu: ( write ) Expected return size: %d\n", s->written); +++ #endif +++ // note: shift left does not work when we shift 64 bits (8 bytes) because the '1' is lost at 65 bits +++ mask = ( s->written >= 8) ? (uint64_t)0xFFFFFFFFFFFFFFFF : ( (uint64_t)0x0000000000000001 << (s->written << 3) ) - 1; +++ s->return_value = *(uint64_t *)(s->mem + RETURN_OFF) & mask; +++ #ifdef GPIO_PT_DEBUG_VERBOSE +++ qemu_printf("qemu: ( write ) Return value 0x%016lX, is copied from raw 0x%016lX, with mask = 0x%016lX\n", s->return_value, *(uint64_t *)(s->mem + RETURN_OFF), mask); +++ #endif +++ } +++ else { +++ // no return value +++ s->written = 0; +++ if ( ret != s->length ) { // size check after write at least 'length' must be written (return padding may occur) +++ qemu_printf("%s: **Warning** %d bytes of %d, were written to host\n", __func__, s->written, s->length); +++ s->return_value = 0x2BADFACE; +++ } +++ pthread_mutex_unlock(&return_mutex); // allow next message +++ } +++ } +++ +++ } // close error check +++ +++ #ifdef GPIO_PT_DEBUG +++ qemu_printf("qemu: ( write --- ) return_value: 0x%016lX\n", s->return_value); +++ #endif +++ } +++ return; +++} +++ +++static const MemoryRegionOps nvidia_gpio_guest_ops = { +++ .read = nvidia_gpio_guest_read, +++ .write = nvidia_gpio_guest_write, +++ .endianness = DEVICE_NATIVE_ENDIAN, +++}; +++ +++static void nvidia_gpio_guest_instance_init(Object *obj) +++{ +++ struct NvidiaGpioGuestState *s = NVIDIA_GPIO_GUEST(obj); +++ memset(s->mem, 0, MEM_SIZE); +++ +++ /* allocate memory map region */ +++ memory_region_init_io(&s->iomem, obj, &nvidia_gpio_guest_ops, s, TYPE_NVIDIA_GPIO_GUEST, MEM_SIZE); +++ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); +++ +++ s->host_device_fd = open(HOST_DEVICE_PATH, O_RDWR); // Open the device with read/write access +++ +++ if (s->host_device_fd < 0) +++ { +++ qemu_printf("%s: **Error** Failed to open the host device..\n", __func__); +++ return; +++ } +++} +++ +++/* create a new type to define the info related to our device */ +++static const TypeInfo nvidia_gpio_guest_info = { +++ .name = TYPE_NVIDIA_GPIO_GUEST, +++ .parent = TYPE_SYS_BUS_DEVICE, +++ .instance_size = sizeof(NvidiaGpioGuestState), +++ .instance_init = nvidia_gpio_guest_instance_init, +++}; +++ +++static void nvidia_gpio_guest_register_types(void) +++{ +++ type_register_static(&nvidia_gpio_guest_info); +++} +++ +++type_init(nvidia_gpio_guest_register_types) +++ +++ /* +++ * Create the Nvidia GPIO guest device. +++ */ +++ DeviceState *nvidia_gpio_guest_create(hwaddr addr) +++{ +++ DeviceState *dev = qdev_new(TYPE_NVIDIA_GPIO_GUEST); +++ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); +++ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); +++ return dev; +++} ++diff --git a/hw/misc/nvidia_gpio_guest.h b/hw/misc/nvidia_gpio_guest.h ++new file mode 100644 ++index 0000000000..9e1a693fb2 ++--- /dev/null +++++ b/hw/misc/nvidia_gpio_guest.h ++@@ -0,0 +1,9 @@ +++#ifndef HW_NVIDIA_GPIO_GUEST_H +++#define HW_NVIDIA_GPIO_GUEST_H +++ +++#include "qom/object.h" +++ +++DeviceState *nvidia_gpio_guest_create(hwaddr); +++ +++ +++#endif ++diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h ++index bb486d36b1..b5ad45d3a3 100644 ++--- a/include/hw/arm/virt.h +++++ b/include/hw/arm/virt.h ++@@ -67,6 +67,8 @@ enum { ++ VIRT_PCIE_MMIO, ++ VIRT_PCIE_PIO, ++ VIRT_PCIE_ECAM, +++ VIRT_NVIDIA_BPMP_GUEST, +++ VIRT_NVIDIA_GPIO_GUEST, ++ VIRT_PLATFORM_BUS, ++ VIRT_GPIO, ++ VIRT_SECURE_UART, ++diff --git a/mk_patch.sh b/mk_patch.sh ++new file mode 100755 ++index 0000000000..bd11d732d0 ++--- /dev/null +++++ b/mk_patch.sh ++@@ -0,0 +1 @@ +++git diff origin/stable-9.0 > qemu-passthrough.patch ++diff --git a/qemu-passthrough.nix b/qemu-passthrough.nix ++new file mode 100644 ++index 0000000000..71135a4d19 ++--- /dev/null +++++ b/qemu-passthrough.nix ++@@ -0,0 +1,143 @@ +++# qemu-passthrough.nix +++# build with command: nix-build qemu-passthrough.nix +++{ pkgs ? import {}, ... }: +++ +++pkgs.stdenv.mkDerivation rec { +++ pname = "qemu-passthrough"; +++ version = "1.1"; +++ +++ src = builtins.path { +++ path = ~/software/Jetson/Linux_for_Tegra/sources/kernel/qemu-passthrough; +++ name = "qemu-source"; +++ }; +++ +++ buildInputs = with pkgs; [ +++ util-linux +++ pkg-config +++ autogen +++ automake +++ flex +++ bison +++ meson +++ ninja +++ cmake +++ json_c +++ gcc +++ gnumake +++ libtool +++ valgrind +++ python3 +++ glib +++ dbus +++ pixman +++ zlib +++ bzip2 +++ lzo +++ libgpiod +++ snappy +++ curl +++ libssh +++ libcap +++ libepoxy +++ nettle +++ attr +++ systemd +++ liburing +++ makeWrapper +++ mktemp +++ libdrm +++ wayland-protocols +++ sphinx +++ python311Packages.sphinx-rtd-theme +++ SDL2 +++ gtk3 +++ gnutls +++ libslirp +++ libselinux +++ alsa-lib +++ alsa-oss +++ pulseaudio +++ pipewire +++ acpica-tools +++ pam_p11 +++ pam_u2f +++ vte +++ libibumad +++ libnfs +++ libseccomp +++ libxkbcommon +++ libcacard +++ libusb1 +++ libaio +++ libcap_ng +++ libtasn1 +++ libgcrypt +++ keyutils +++ canokey-qemu +++ fuse3 +++ libbpf +++ capstone +++ fdtools +++ vde2 +++ texinfo +++ spice +++ virglrenderer +++ multipath-tools +++ ncurses +++ sealcurses +++ lzfse +++ gsasl +++ xgboost +++ libvncserver +++ cmocka +++ basez +++ SDL2 +++ SDL2_image +++ ceph +++ gsasl +++ xdp-tools +++ fdtools +++ dtc +++ ]; +++ +++ configurePhase = '' +++ ./configure --target-list=aarch64-softmmu \ +++ --ena +\ No newline at end of file