Update tooling for custom kernels (#6)

Improve support for custom device trees when kernel updates are received

* Modified make-trees.sh so that it builds for all installed kernels.
* A new script flash-latest.sh that copies the resulting .dtb files into the /etc/flash-kernel/dtbs/ override folder.

The build-trees README is updated for this, and shows how to configure flash-kernel to use the custom .dtbs.

Kernel upgrade should now go:

[after a new kernel upgrade is installed]
* Upgrade source: cd source && apt source linux-riscv
* Build dtb's: cd build-trees &&; ./make_dtbs.sh
* Install device tree via softlinks ./flash_latest.sh
* Reboot
This commit is contained in:
Owen Carter
2024-09-19 14:52:55 +02:00
committed by GitHub
parent 9f11caaf73
commit 520118f143
18 changed files with 1469 additions and 240 deletions
+83 -59
View File
@@ -1,6 +1,6 @@
# Building and installing custom device trees.
This folder contains a `make-trees` script that can build device tree source (`.dts`) files against the correct upstream headers and device tree includes.
This folder contains a `make_dtbs` script that can build device tree source (`.dts`) files against the correct upstream headers and device tree includes.
## Preparation / requirements
@@ -34,9 +34,10 @@ You should see a load of new (source) repos being updated, it is slow, let it fi
### Get the linux sources
This should be done as a normal user
- Note that the command used here `apt source` will download the sources to the current working folder, not a fixed location, and is intended to be run as a normal user.
- Note that the command used here `apt source` will download the sources to the current working folder, not a fixed location
- It is run as a normal user, not root.
We download the sources into the [sources](../sources) repo in this folder:
We download the sources into the [sources](../sources) folder:
```text
$ cd source
$ apt source linux-riscv
@@ -50,17 +51,19 @@ You can re-run the `apt source` command in this folder and it will only download
-------------------------------------------
# Building the device tree(s)
Build the sources as a normal user (the same user used to fetch the sources above), in this folder.
Run the build as a normal user (the same user used to fetch the sources above).
- Put your custom `.dts` in this folder.
- This folder also contains the necesscary softlinks to the include files in the source tree we just downloaded.
#### Terms
* `.dts` is a top-level Device Tree Source file.
* `.dtsi` is a include file for the `.dts`
* `.dtsi` is an include file for the `.dts`
* `.dtb` is the binary compiled device tree, this is what we are building here, and is supplied to the kernel at boot time.
## device tree sources
By default the standard `sun20i-d1-mangopi-mq-pro.dts` file from the Ubuntu source is linked here.
For convenience, the default `sun20i-d1-mangopi-mq-pro.dts` file from the Ubuntu source is also linked here.
Rather than modifying the default tree you should copy it to a custom name, eg 'my-project-mqpro.dts'. Or you can copy in examples from the [alt-trees](../alt-trees/) folder.
Rather than modifying the default tree you should copy it to a custom name, eg 'my-project.dts'. Or you can copy in examples from the [alt-trees](../alt-trees/) folder.
A full-on tutorial for device tree editing is far beyond the scope of both this document and author.
* The examples show some simple custom modifications.
@@ -69,17 +72,19 @@ A full-on tutorial for device tree editing is far beyond the scope of both this
## Compile the mq-pro dts with the current kernel headers
To compile all the includes and sources simply run `make-trees.sh`.
To compile all the includes and sources simply run `make_dtbs.sh`.
This will:
* Create an output folder named after the kernel version, or clean an existing output folder.
* Pre-compile all the source and include files in the current folder into the output folder using the current kernel headers.
* In the output folder it then compiles *all* the `.dts` files present, and prefixes the output `.dtb` files with the kernel version.
* Pre-compile all the source and include files in the current folder into the output folder using the correct kernel headers.
* In the output folder it then compiles *all* the `.dts` files present.
```console
$ ./make_dtb.sh
$ ./make_dtbs.sh
Building for kernels: 6.8.0-41-generic
Cleaning existing 6.8.0-41-generic build directory
Compiling against headers for 6.8.0-41-generic
Creating new build directory: 6.8.0-41-generic
Precompiling all includes in build root into 6.8.0-41-generic build directory
sun20i-common-regulators.dtsi -> 6.8.0-41-generic/sun20i-common-regulators.dtsi
sun20i-d1.dtsi -> 6.8.0-41-generic/sun20i-d1.dtsi
@@ -87,23 +92,30 @@ Precompiling all includes in build root into 6.8.0-41-generic build directory
sunxi-d1-t113.dtsi -> 6.8.0-41-generic/sunxi-d1-t113.dtsi
sunxi-d1s-t113.dtsi -> 6.8.0-41-generic/sunxi-d1s-t113.dtsi
Precompiling all sources in build root into 6.8.0-41-generic build directory
my-project-mqpro.dts -> 6.8.0-41-generic/my-project-mqpro.dts
my-project.dts -> 6.8.0-41-generic/my-project.dts
sun20i-d1-mangopi-mq-pro.dts -> 6.8.0-41-generic/sun20i-d1-mangopi-mq-pro.dts
Compiling all device tree sources in 6.8.0-41-generic build directory
6.8.0-41-generic/my-project-mqpro.dts -> 6.8.0-41-generic/6.8.0-41-generic-my-project-mqpro.dtb
6.8.0-41-generic/sun20i-d1-mangopi-mq-pro.dts -> 6.8.0-41-generic/6.8.0-41-generic-sun20i-d1-mangopi-mq-pro.dtb
6.8.0-41-generic/my-project.dts -> 6.8.0-41-generic/my-project.dtb
6.8.0-41-generic/sun20i-d1-mangopi-mq-pro.dts -> 6.8.0-41-generic/sun20i-d1-mangopi-mq-pro.dtb
Success. Consider running 'flash_latest.sh' to make permanent (see docs)
```
The `6.8.0-41-generic` folder now has our device tree: `my-project.dtb`
- We also generate the default device tree, this can be ignored.
The tool builds for *all* the kernels available on the system, not just the running kernel.
- As new kernels are updated the list of 'available' kernels will increase.
-----------------------
# Test Installing self-built DTB's
If this is the first time the tree is compiled after modifying it may be a good idea to do a 'quick' test that it boots properly before making it permanent.
### Move dtb into the boot tree
* move the `.dtb` file into the `/boot` folder: eg: `$ sudo mv 6.8.0-41-generic-my-project-mqpro.dtb /boot/dtbs`
* make a soft link in `/boot` to this: `$ sudo ln -s dtbs/6.8.0-41-generic-my-project-mqpro.dtb /boot/dtb-mqpro`
* move the `.dtb` file into the `/boot` folder: eg: `$ sudo mv 6.8.0-41-generic/my-project.dtb /boot/dtbs`
* make a soft link in `/boot` to this: `$ sudo ln -s dtbs/my-project.dtb /boot/dtb-mqpro`
### Set up Grub to test boot the new DTB
Initially we will test the new dtb:
* backup the grub config: `$ sudo cp /etc/grub/grub.cfg /etc/grub/grub.cfg.mybackup`
* `$ sudo vi /etc/grub/grub.cfg` (or use nano if you prefer)
* Find the 1st `menuentry` section (the default Ubuntu one) and edit the `devicetree` line to look like:
@@ -112,64 +124,77 @@ devicetree /boot/dtb-mqpro
```
* Reboot (`$ sudo reboot`)
* If the reboot fails you can either attach a serial adapter to the GPIO pins and select the fallback kernel from the advanced options menu, and then restore the grub config backup once logged in.
Or (if no serial available) remove the SD card, mount it on another computer and restore the file there.
Or (if no serial available) remove the SD card, mount it on another (unix) computer and restore the grub config there.
After rebooting you can run **list-pins.py** (see below) to verify the new mappings.
If you have errors rebooting (maybe a corrupt file if you rebuilt it etc..) you need to either boot using a USB serial adapter on the console pins and select the recovery image, or, in grub, edit the command and revert to the generic `/boot/dtb`.
As a last resort you may have to remove the SD card, mount the `/boot` partition and edit `grub/grub.cfg` there.
* !! The 'default' dtb supplied by ubuntu should always be softlinked as `/boot/dtb`, so putting `devicetree /boot/dtb` in grub in place of the custom `.dtb` should work and is predictable (no version numbers etc).
## Examining the DTB pin mappings with `list-pins.py`
In the [tools](../tools) folder there is a python script called `list-pins.py`.
To run the pin list tool you need to be in the tools directory, then run:
```console
$ python3 list-pins.py MangoPi-MQ-Pro
```
* The script requires root acces (via sudo) to read the pin maps.
* Running the script produces the same map I use in this documentation.
* The data used to assemble the `.gpio` map files identifies which interface a pin is attached to, but not it's specific function for the interface.
* eg it can say 'pinX and pinY are mapped to UART2', but cannot identify which pin is the TX and which is the RX; a limitation of the data, my apologies..
* You therefore need to reference the [D1 pin mapping table](../reference/d1-pins.pdf) to get the exact functions for pins when running this for yourself.
* The README files uploaded for alternate device trees *have been manually edited* to note full pin function for convenience.
After rebooting you can run **list-pins.py** to verify the new mappings.
* See the README in the [tools](../tools) folder for usage.
### Cleanup test
Once you are happy with the test you should make the change permanent as described below.
* Before you do the permanent install you *must* restore the backup copy of the grub config: `$ sudo mv /etc/grub/grub.cfg.mybackup /etc/grub/grub.cfg`
* Once that is done you can also clean up any test `.dtb` files you manually placed in `/boot/`, and the softlink to them.
* *Do not remove the files without restoring the grub config, it will leave the system unbootable!*
* Once that is done you can also clean up the `.dtb` file you manually placed in `/boot/`, and the softlink to it.
* *Do not remove the device tree file without restoring the grub config, it will leave the system unbootable!*
----------------------------------------------------
# Making Permanent:
We can use [flash-kernel](https://github.com/ubports/flash-kernel) to permanently apply our custom device tree. *Flash-kernel* allows an 'override' device tree to be specified that will be used in place of the tree provided by the linux firmware package.
We can use [flash-kernel](https://github.com/ubports/flash-kernel) to permanently apply our custom device tree.
* *Flash-kernel* normally searches in the linux firmware library to select the matching kernel version of the `.dtb` file for the machine (as specified in the database).
* But if a file of the same name is found in the `dtbs` override directory this will be used instead.
*Flash-kernel* normally searches in the linux firmware library to select the matching kernel version of the `.dtb` file for the machine (as specified in the database). But if a file of the same name is found in the `dtbs` override directory this will be used instead.
If we soft-link our custom `.dtb` file from this directory and re-run `flash-kernel` it will be installed to the `/boot/dtbs/` tree and used at next boot.
- As with all the device tree tests above an error here might produce an unbootable machine!
## Configure `flash-kernel` for the cusom device tree file
Similar to the way we reconfigured *flash-kernel* to use the MQ Pro device tree in the install guide, we can also configure it to use our custom kernel.
Add a new entry to `/etc/flash-kernel/db`:
```text
$ cd /etc/flash-kernel/dtbs/
$ sudo ln -s /home/<user+path>/MQ-Pro-IO/build-trees/6.8.0-41-generic/6.8.0-41-generic-my-project-mqpro.dtb sun20i-d1-mangopi-mq-pro.dtb
# Custom project entry
Machine: My Project
Kernel-Flavors: any
DTB-Id: custom/my-project.dtb
Boot-Script-Path: /boot/boot.scr
U-Boot-Script-Name: bootscr.uboot-generic
Required-Packages: u-boot-tools
```
In this example I am soft linking directly to the `.dtb` I built.
Note that we specify `custom` in the DTB-Id instead of 'allwinner', this helps keep our trees apart from the vanilla (Ubuntu) ones in the boot tree.
Run `flash-kernel` again: you will see the overide being applied.
Edit `/etc/flash-kernel/machine` to match the machine name in the definition:
```console
$ sudo flash-kernel
Using DTB: allwinner/sun20i-d1-mangopi-mq-pro.dtb
Installing /etc/flash-kernel/dtbs/sun20i-d1-mangopi-mq-pro.dtb into /boot/dtbs/6.8.0-41-generic/allwinner/sun20i-d1-mangopi-mq-pro.dtb
Taking backup of sun20i-d1-mangopi-mq-pro.dtb.
Installing new sun20i-d1-mangopi-mq-pro.dtb.
$ sudo mv /etc/flash-kernel/machine /etc/flash-kernel/machine.vanilla
$ sudo sh -c "echo 'My Project' > /etc/flash-kernel/machine"
```
Running *flash-kernel* immediately after this will fail since it cannot yet find the `.dtb` file specified in the database.
- We need to copy the `.dtb` to `/etc/flash-kernel/dtbs` first.
## Copying and flashing the device tree file
Run `flash_latest.sh`, this will ask you to confirm which kernel version you want to copy from.
- It defaults to the current running kernel.
- When upgrading this allows you to precompile and install the correct DTB in advance before rebooting into the new kernel.
```console
$ ./flash_latest.sh
Available kernels:
[1] 6.8.0-41-generic - currently running kernel
Which kernel to link? [1]:
Cleaning '/etc/flash-kernel/dtbs/' and copying in device tree binaries from '6.8.0-41-generic/'
/home/owen/MQ-Pro-IO/build-trees/6.8.0-41-generic/my-project.dtb --> /etc/flash-kernel/dtbs/my-project.dtb
/home/owen/MQ-Pro-IO/build-trees/6.8.0-41-generic/sun20i-d1-mangopi-mq-pro.dtb --> /etc/flash-kernel/dtbs/sun20i-d1-mangopi-mq-pro.dtb
Run 'flash-kernel' to apply device tree? [Y]:
Using DTB: custom/my-project.dtb
Installing /etc/flash-kernel/dtbs/my-project.dtb into /boot/dtbs/6.8.0-41-generic/custom/my-project.dtb
Taking backup of my-project.dtb.
Installing new my-project.dtb.
System running in EFI mode, skipping.
If flash-kernel was successful and configured properly the new device tree will be used after reboot
```
After this, reboot to use the new device tree.
A *flash-kernel* installs a full copy of the `.dtb` into the `/boot/` area, so deleting or moving the build folder will not 'break' bootup, but it *will* break kernel image rebuilds when `dpkg` tries to re-run the *flash-kernel* command and the softlink target has disappeared. Be warned!
It is good practice to update the dtb when new kernels become available. But Ubuntu 24.04.1 is a LTS release, and the DTB should be stable going forward so you may not find it necesscary.
It is good practice to keep the build repo and (periodically) update the dtb when new kernels becme available. But Ubuntu 24.04.1 is a LTS release, and the DTB should be stable going forward so you may not find it necesscary.
To Update, re-fetch the latest sources (see above), then re-run `make_dtbs.sh` and `flash_latest.sh`.
--------------------------------------------------------------------------
@@ -181,4 +206,3 @@ It is good practice to keep the build repo and (periodically) update the dtb whe
Device Tree that is used in the official armbian image?
- https://github.com/smaeul/u-boot/tree/329e94f16ff84f9cf9341f8dfdff7af1b1e6ee9a/arch/riscv/dts
+60
View File
@@ -0,0 +1,60 @@
#!/bin/bash
# Allows the user to choose a kernel version and copies .dtb files to /etc/flash-kernel
# - defaults to current highest available kernel version
#
cdir=`pwd`
versions=`dpkg --list | grep linux-image-[0-9].* | cut -d" " -s -f 3 | sed s/^linux-image-// | sort -V`
current=`/usr/bin/uname -r`
out=/etc/flash-kernel/dtbs
echo -e "\nAvailable kernels:"
option=0
declare -a klist
for ver in $versions ; do
option=$((option+1))
klist[$option]=$ver
echo -n " [$option] $ver"
if [ $ver == $current ] ; then
echo " - currently running kernel"
else
echo
fi
done
read -p "Which kernel to link? [$option]: " choice
if [ -z "$choice" ] ; then choice=$option ; fi
echo
revision=${klist[$choice]}
if [ -z "$revision" ] ; then
echo "No valid kernel selected, exiting."
exit
fi
if [ -d "$revision" ]; then
echo -e "Cleaning '$out/' and copying in device tree binaries from '$revision/'"
sudo rm -f $out/*.dtb $out/origin*
else
echo "No builds found for selected kernel version: $revision"
echo " Try running ./make_trees.sh to generate them"
exit 1
fi
for file in `cd "$revision" ; ls *.dtb`; do
echo " $cdir/$revision/$file --> $out/$file"
sudo cp $cdir/$revision/$file $out
done
# Add a link to the output folder..
sudo ln -s $cdir/$revision $out/origin:$revision
echo
read -p "Run 'flash-kernel' to apply device tree? [Y]: " choice
if [[ "$choice" == [Yy]* ]] || [ -z "$choice" ] ; then
sudo flash-kernel
echo -e "\nIf flash-kernel was successful and configured properly the new device tree will be used after reboot"
else
echo "The new device tree will be used the next time flash-kernel is run"
fi
# fin
-43
View File
@@ -1,43 +0,0 @@
#!/bin/bash
# Takes a list of dts files for the specified architecture and emits appropriate compiled dtb files for use in customised deviceTree setups.
#
dtc=/usr/bin/dtc
revision=`/usr/bin/uname -r`
# alt=../alt-trees -- disabled auto building of all alt trees, better to do individually
echo "Compiling against headers for $revision"
if [ -d "$revision" ]; then
echo "Cleaning and Using existing build directory"
rm $revision/*.dts $revision/*.dtsi $revision/*.dtb
else
echo "Creating new build directory: $revision"
mkdir "$revision"
fi
#echo "Copying custom dts sources to build root"
#for dts in `ls -d $alt/*/*.dts`; do
# echo "$dts"
# cp $dts .
#done
echo "Precompiling all includes in build root into $revision build directory"
for file in `ls *.dtsi`; do
echo " $file -> $revision/${file##*/}"
cpp -I/usr/src/linux-headers-$revision/include/ -nostdinc -undef -x assembler-with-cpp $file > $revision/${file##*/}
done
echo "Precompiling all sources in build root into $revision build directory"
for file in `ls *.dts`; do
echo " $file -> $revision/${file##*/}"
cpp -I/usr/src/linux-headers-$revision/include/ -nostdinc -undef -x assembler-with-cpp $file > $revision/${file##*/}
done
echo "Compiling all device tree sources in $revision build directory"
cd $revision
for file in `ls *.dts`; do
out=${file/.dts/.dtb}
echo " $revision/$file -> $revision/$revision-$out"
$dtc $file > $revision-$out
done
+76
View File
@@ -0,0 +1,76 @@
#!/bin/bash
# Takes a list of dts files for the specified architecture and emits appropriate compiled dtb files for use in customised deviceTree setups.
#
dtc=/usr/bin/dtc
cdir=`pwd`
versions=`dpkg --list | grep linux-image-[0-9].* | cut -d" " -s -f 3 | sed s/^linux-image-// | sort -V`
echo -ne "\nBuilding for kernels: "
echo `echo $versions | sed "s/ /, /g"`
echo
# Disabled auto building of all alt trees, better to do individually
#alt=../alt-trees
#echo "Linking alt dts sources to build root"
#for dts in `ls -d $alt/*/*.dts`; do
# echo "$dts"
# ln -s "$dts" .
#done
# Ensure sure build roots exist and are clean
for revision in $versions ; do
if [ -d "$cdir/$revision" ]; then
echo "Cleaning existing $revision build directory"
rm -f $revision/*.dts $revision/*.dtsi $revision/*.dtb
else
echo "Creating new build directory: $revision"
mkdir $revision
fi
done
# Compile for each revision
for revision in $versions ; do
cd $cdir
echo -e "\nCompiling against headers for $revision"
echo "Precompiling all includes in build root into $revision build directory"
for file in `ls *.dtsi`; do
echo " $file -> $revision/${file##*/}"
cpp -I/usr/src/linux-headers-$revision/include/ -nostdinc -undef -x assembler-with-cpp $file > $revision/${file##*/}
if [ ! -s "$revision/${file##*/}" ] ; then
rm "$revision/${file##*/}"
echo "**** ERROR ****"
echo "Precompile failed for include: $revision/${file##*/}"
exit 1
fi
done
echo "Precompiling all sources in build root into $revision build directory"
for file in `ls *.dts`; do
echo " $file -> $revision/${file##*/}"
cpp -I/usr/src/linux-headers-$revision/include/ -nostdinc -undef -x assembler-with-cpp $file > $revision/${file##*/}
if [ ! -s "$revision/${file##*/}" ] ; then
rm "$revision/${file##*/}"
echo "**** ERROR ****"
echo "Precompile failed for source: $revision/${file##*/}"
exit 1
fi
done
echo "Compiling all device tree sources in $revision build directory"
cd $revision
for file in `ls *.dts`; do
out=${file/.dts/.dtb}
echo " $revision/$file -> $revision/$out"
$dtc $file > $out
if [ ! -s "$out" ] ; then
rm "$out"
echo "**** ERROR ****"
echo "Compile failed for: $out"
exit 1
fi
done
done
echo -e "\nSuccess. Consider running 'flash_latest.sh' to make permanent (see docs)"