How to limit the Framework Laptop's battery charge on Linux

24/02/2024

Building and installing framework-laptop-kmod to limit the Framework Laptop's battery charge (On Ubuntu with Secure Boot enabled)

NOTE: This works, but will break each time the kernel gets updated (you need to delete `framework_laptop.ko`, run `make` and then sign the module with the old keys, then install and load), so I will update the guide in the future to use DKMS as illustrated in this gist

Clone the project and make sure the parent directories don’t have spaces in the name, because make will break!

git clone https://github.com/DHowett/framework-laptop-kmod.git

Enter the directory and build:

cd framework-laptop-kmod
make

The directory should now contain the following files:

dkms.conf
framework_laptop.c
framework_laptop.ko     # The compiled kernel module!
framework_laptop.mod
framework_laptop.mod.c
framework_laptop.mod.o
framework_laptop.o
LICENSE
Makefile
modules.order
Module.symvers
README.md

Check if Secure Boot is active

mokutil --sb-state

If it’s active, you’ll need to complete a couple of more steps to sign the kernel module to load it successfully.

If it isn’t you can skip the following section!

Sign the kernel module

This part is directly taken from here, credit to Mathieu Trudel-Lapierre, I’m just reporting the relevant parts here.

Create a file in the current directory containing this openssl config (edit the req_distinguished_name section at will):

# This definition stops the following lines choking if HOME isn't
# defined.
HOME                    = .
RANDFILE                = $ENV::HOME/.rnd 
[ req ]
distinguished_name      = req_distinguished_name
x509_extensions         = v3
string_mask             = utf8only
prompt                  = no

[ req_distinguished_name ]
countryName             = US                            # <- CHANGE ME
stateOrProvinceName     = InsertStateOrProvince         # <- CHANGE ME
localityName            = InsertLocalityName            # <- CHANGE ME
0.organizationName      = insertorgname                 # <- CHANGE ME
commonName              = Secure Boot Signing
emailAddress            = [email protected]

[ v3 ]
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always,issuer
basicConstraints        = critical,CA:FALSE
extendedKeyUsage        = codeSigning,1.3.6.1.4.1.311.10.3.6,1.3.6.1.4.1.2312.16.1.2
nsComment               = "OpenSSL Generated Certificate"

Here the command is set up for a config file called openssl.cnf, but you can call it whatever you like

openssl req -config ./openssl.cnf \
        -new -x509 -newkey rsa:2048 \
        -nodes -days 36500 -outform DER \
        -keyout "MOK.priv" \
        -out "MOK.der"

Create a key enrolling request:

sudo mokutil --import MOK.der

It will ask to create a password, which will be used in a moment.

REBOOT, MokManager will start up and ask to enroll the key and to insert the password that you just created.

Once you are back in your OS, look at the output of

sudo cat /proc/keys

or

mokutil --list-enrolled

You should be able to see the key that was generated

Then, finally, the kernel module can be signed (cd in the git repository):

kmodsign sha512 MOK.priv MOK.der framework_laptop.ko

We can then check that the module.ko file is indeed signed by running

modinfo ./framework_laptop.ko

or

hexdump -Cv framework_laptop.ko | tail -n 5

Install the module system wide

sudo cp framework_laptop.ko /lib/modules/`uname -r`

Load the kernel module

Rebuild the module index so that modprobe can find it

sudo depmod

Load the module

sudo modprobe framework_laptop

Persist module loading across reboots

Just add the module name (framework_laptop) to /etc/modules, which has a symbolic link to the actual location which is /etc/modules-load.d/modules.conf

The End

Now you can use the GNOME extension Battery Health Charging to change the max battery charging percentage (a log out may be required)

All posts
davespace.xyz – 2024