blog: nixos-kernel-hacking: finish the rest of the article

This commit is contained in:
2024-11-15 16:43:02 +00:00
parent 14f41027ee
commit f9c9c9254d

View File

@@ -101,7 +101,7 @@ this patch can alternately be represented as a [Device Tree Overlay] (DTO):
// instruct the system to only apply this overlay
// to PinePhone Pro, and not any other devices.
/ {
compatible = "pine64,pinephone-pro";
compatible = "pine64,pinephone-pro";
};
// address the parent node we want to patch,
@@ -109,7 +109,7 @@ this patch can alternately be represented as a [Device Tree Overlay] (DTO):
// this overrides `press-threshold-microvolt`
// while leaving all other properties unchanged.
&{/adc-keys/button-down} {
press-threshold-microvolt = <400000>;
press-threshold-microvolt = <400000>;
};
```
@@ -155,15 +155,173 @@ then, we can ship new kernel modules by:
### Building a Kernel Module
TODO
my kernel module has 3 files, which live in my nix repo inline:
- [rk818_battery.c](https://git.uninsane.org/colin/nix-files/src/commit/4e008c34208143a489d824b288437201f7137ace/pkgs/linux-packages/rk818-charger/rk818_battery.c)
- [rk818_battery.h](https://git.uninsane.org/colin/nix-files/src/commit/4e008c34208143a489d824b288437201f7137ace/pkgs/linux-packages/rk818-charger/rk818_battery.h)
- [rk818_charger.c](https://git.uninsane.org/colin/nix-files/src/commit/4e008c34208143a489d824b288437201f7137ace/pkgs/linux-packages/rk818-charger/rk818_charger.c)
these are compiled into `rk818_battery.ko` and `rk818_charger.ko` by a Makefile:
```make
obj-m := rk818_battery.o rk818_charger.o
all:
$(MAKE) -C "$(KERNEL_DIR)" M="$(PWD)" modules
install:
install -Dm444 rk818_battery.ko $(INSTALL_MOD_PATH)/drivers/power/supply/rk818_battery.ko
install -Dm444 rk818_charger.ko $(INSTALL_MOD_PATH)/drivers/power/supply/rk818_charger.ko
```
this can be packaged for nix as so:
```nix
# file: pkgs/linux-packages/rk818-charger/default.nix
{
buildPackages,
kernel,
lib,
stdenv,
}: stdenv.mkDerivation {
pname = "rk818-charger";
version = "0-unstable-2024-10-01";
src = ./.;
hardeningDisable = [ "pic" ];
nativeBuildInputs = kernel.moduleBuildDependencies;
makeFlags = [
"KERNEL_DIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build"
"INSTALL_MOD_PATH=$(out)/lib/modules/${kernel.modDirVersion}/kernel"
# from pkgs/os-specific/linux/kernel/manual-config.nix:
"O=$(buildRoot)"
"CC=${stdenv.cc}/bin/${stdenv.cc.targetPrefix}cc"
"HOSTCC=${buildPackages.stdenv.cc}/bin/${buildPackages.stdenv.cc.targetPrefix}cc"
"HOSTLD=${buildPackages.stdenv.cc.bintools}/bin/${buildPackages.stdenv.cc.targetPrefix}ld"
"ARCH=${stdenv.hostPlatform.linuxArch}"
] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
"CROSS_COMPILE=${stdenv.cc.targetPrefix}"
];
# NixOS kernel expects compressed kernel modules, so do that here.
postInstall = ''
find $out -name '*.ko' -exec xz {} \;
'';
}
```
then add this package to the `linuxKernel` package set:
```nix
{ ... }:
{
nixpkgs.overlays = [(self: super: {
linuxKernel = super.linuxKernel // {
packagesFor = kernel:
(
super.linuxKernel.packagesFor kernel
).extend (kFinal: kPrev: with kFinal; {
rk818-charger = callPackage ./pkgs/linux-packages/rk818-charger { };
});
};
})];
}
```
kernel modules must be built against the same kernel version
that they'll be loaded by: this `linuxKernel` package set looks
funny at first, but it exists to make that easier.
your kernel module can be built like this:
- `nix-build -A linuxPackages_6_11.rk818-charger`
- `nix-build -A linuxPackages_6_10.rk818-charger`
and so on, depending on which kernel you're running.
### Deploying a Kernel Module
TODO
add the following to your NixOS config:
```nix
{ config, ... }:
{
# this builds our modules against the specific kernel in use by the host
# and makes them available under /run/current-system/kernel-modules to tools like `modprobe`
boot.extraModulePackages = [
config.boot.kernelPackages.rk818-charger
];
# explicitly load the modules during stage-2 boot.
# if they're needed earlier in the boot process, use `boot.initrd.kernelModules` instead.
boot.kernelModules = [
"rk818_battery"
"rk818_charger"
];
}
```
now we've recreated the same behavior as if the kernel module had been shipped in-tree,
but we still have to associate the driver with a specific device if we want it to do anything.
### Device Tree + Module Integration
TODO
add the following Device Tree Overlay to `hardware.deviceTree.overlays`,
as we did earlier with the battery DTO:
```dts
/dts-v1/;
/plugin/;
// apply the overlay only to PinePhone Pro; no other devices.
/ {
compatible = "pine64,pinephone-pro";
};
&{/} {
bat: battery {
compatible = "simple-battery";
voltage-min-design-microvolt = <3400000>;
voltage-max-design-microvolt = <4350000>;
};
};
&rk818 {
battery {
// this line associates this device with the rk818-battery module
// we just shipped
compatible = "rockchip,rk818-battery";
// constants were taken from the pine64-org kernel tree:
ocv_table = <3400 3675 3689 3716 3740 3756 3768 3780
3793 3807 3827 3853 3896 3937 3974 4007 4066
4110 4161 4217 4308>;
design_capacity = <2916>;
design_qmax = <2708>;
bat_res = <150>;
max_input_current = <3000>;
max_chrg_current = <2000>;
max_chrg_voltage = <4350>;
sleep_enter_current = <300>;
sleep_exit_current = <300>;
power_off_thresd = <3400>;
zero_algorithm_vol = <3950>;
fb_temperature = <105>;
sample_res = <10>;
max_soc_offset = <60>;
energy_mode = <0>;
monitor_sec = <5>;
virtual_power = <0>;
power_dc2otg = <0>;
otg5v_suspend_enable = <1>;
};
charger {
// this line associates this device with the rk818-charger module
// we just shipped
compatible = "rockchip,rk818-charger";
monitored-battery = <&bat>;
};
};
```
## Patching an Upstream Kernel Module