KERNEL
Continuiamo la nostra avventura
affrontando l’ostacolo più grosso.
Il kernel è dove succede la vera magia.
Il problema principale è destreggiarsi
tra i principali strumenti che lo rendono
così configurabile:
il device-tree, i driver e i kconfig.
Le distribuzioni Yocto solitamente usano
un fork kernel Linux originale,
integrandoci degli ulteriori meccanismi
di configurazione.
Al momento però il kernel linux-yocto
non include la logica e la definizioni necessarie
per pilotare le schede della famiglia stm32mp2.
Potremmo cambiare SRC_URI per puntare alla repo di stm
come abbiamo fatto per gli altri recipe,
ma incontreremmo tutta una serie di errori di Yocto
in quanto nei recipe che si occupano del kernel
sono implementati tutta una serie di controlli
sulla validità del SRC_URI.
La scelta più conveniente è quindi di creare un nuovo recipe
e includere la logica di base per il kernel fornita da Yocto.
recipe iniziale
Possiamo creare un file .bb da zero
oppure utilizzare recipetool per creare un .bb
con già un punto di partenza decente:
recipetool create -o /workdir/layers/yoctolearn/meta-stm32mp2/recipes-kernel/linux/linux-stm.bb https://github.com/STMicroelectronics/linux.gitè sufficiente fornire il path del recipe e url della repo che vogliamo usare.
1SUMMARY = "An example kernel recipe that uses the linux-yocto and oe-core"
2# linux-yocto-custom.bb:
3#
4# kernel classes to apply a subset of yocto kernel management to git
5# managed kernel repositories.
6#
7# To use linux-yocto-custom in your layer, copy this recipe (optionally
8# rename it as well) and modify it appropriately for your machine. i.e.:
9#
10# COMPATIBLE_MACHINE:yourmachine = "yourmachine"
11#
12# You must also provide a Linux kernel configuration. The most direct
13# ...
14# example feature addition (for kernel v4.x only):
15# SRC_URI += "file://feature.scc"
16#
17
18inherit kernel
19require recipes-kernel/linux/linux-yocto.inc
20
21SRC_URI = "git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git;protocol=git;nocheckout=1;name=machine"
22
23LINUX_VERSION ?= "5.0.0-rc4"
24LINUX_VERSION_EXTENSION:append = "-custom"
25
26SRCREV_machine= "62967898789dc1f09a06e59fa85ae2c5ca4dc2da"
27
28PV = "${LINUX_VERSION}+git"
29
30COMPATIBLE_MACHINE = "stm32mp257f-dk"
31# WARNING: the following LICENSE and LIC_FILES_CHKSUM values are best guesses - it is
32# your responsibility to verify that the values are complete and correct.
33#
34# The following license files were not able to be identified and are
35# ...
36# instead of &. If there is any doubt, check the accompanying documentation
37# to determine which situation is applicable.
38LICENSE = "LGPL-2.0-only & Unknown"
39LIC_FILES_CHKSUM = "file://COPYING;md5=bbea815ee2795b2f4230826c0c6b8814 \
40 file://Documentation/networking/device_drivers/qlogic/LICENSE.qla3xxx;md5=ae5c998421760d17dd805a57f798a82b \
41 file://Documentation/networking/device_drivers/qlogic/LICENSE.qlcnic;
42 ...
43 file://tools/usb/usbip/COPYING;md5=1f6f1c0be32491a0c8d2915607a28f36"Vediamo che bitbake capisce che si tratta del kernel
analizzando i contenuti della repo che stiamo usando
e aggiunge delle variabili rilevanti al nostro recipe.
COMPATIBLE_MACHINE = "stm32mp257f-dk" ci va bene,
però SRC_URI fa riferimento alla versione mainline di linux
e a noi non va bene.
Utile che abbia inserito anche i dati della licenza,
però non possiamo usarli perchè si riferiscono
ai file presenti nella repo sbagliata.
In caso di dubbi sulle variabili ricordiamoci che esiste il glossario, controllando che sia relativo alla versione Yocto che stiamo usando. Yocto fornisce anche una lista di variabili importanti per kernel.
Modifichiamo quindi il recipe:
SRC_URI = "git://github.com/STMicroelectronics/linux.git;branch=v6.6-stm32mp;protocol=https"
## tag: v6.6-stm32mp-r2
SRCREV = "f01241fbba4d879fa770685629b49d42e904e43c"
PV := "v6.6-stm32mp"
PR := "r2"
LINUX_VERSION = "6.6"
LINUX_VERSION_EXTENSION:append = "-stm32"provando a buildare vedremo una serie di errori:
ERROR: Nothing PROVIDES 'linux-stm'
linux-stm was skipped: Set PREFERRED_PROVIDER_virtual/kernel to linux-stm to enable itquindi in machine.conf inseriamo
PREFERRED_PROVIDER_virtual/kernel = "linux-stm"poi
ERROR: linux-stm-tut-v6.6-stm32mp-r2 do_kernel_metadata: Could not locate BSP definition for stm32mp257f-dk/standard and no defconfig was providedquindi nel recipe inseriamo
KBUILD_DEFCONFIG = "defconfig"poi
ERROR: linux-stm-tut-v6.6-stm32mp-r2 do_kernel_version_sanity_check: Package Version (v6.6-stm32mp) does not match of kernel being built (6.6.78). Please update the PV variable to match the kernel source or set KERNEL_VERSION_SANITY_SKIP="1" in your recipe.che possiamo risolvere definendo
PV := "6.6.78"oppure disabilitando l’errore:
KERNEL_VERSION_SANITY_SKIP = "1"poi
| make[1]: *** No rule to make target 'zImage'. Stop.il kernel viene compilato in un file compresso chiamato zImage,
ma per architettura arm64 questo non va bene
e bisogna usare la variante non compressa Image
KERNEL_IMAGETYPE = "Image"poi dopo la prima build vedremo un errore relativo alla licenza e bitbake ci fornirà il valore del checksum da utilizzare:
ERROR: linux-stm-6.6.78+git-r2 do_populate_lic: QA Issue: linux-stm: The LIC_FILES_CHKSUM does not match for file://COPYING;md5=d7810fab7487fb0aad327b76f1be7cd7
linux-stm-tut: The new md5 checksum is 6bc538ed5bd9a7fc9398086aedcd7e46quindi
LIC_FILES_CHKSUM = "file://COPYING;md5=6bc538ed5bd9a7fc9398086aedcd7e46"definendo anche il devicetree da utilizzare, possiamo utilizzare la classe yocto-kernel per compilare il dts in un dtb:
## by default dt are searched inside "arch/<ARCH>/boot/dts/" dir
## KERNEL_DEVICETREE is a relative path from this dir
## and must end with .dtb extension
KERNEL_DEVICETREE = "st/stm32mp257f-dk.dtb"otteniamo così un recipe funzionante:
SUMMARY = "Linux STM32MP Kernel"
SECTION = "kernel"
LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://COPYING;md5=6bc538ed5bd9a7fc9398086aedcd7e46"
## inherits kernel and kernel-yocto
require recipes-kernel/linux/linux-yocto.inc
S = "${WORKDIR}/git"
UNPACKDIR = "${S}"
## ================================================================
## STM FORK
## ================================================================
SRC_URI = "git://github.com/STMicroelectronics/linux.git;branch=v6.6-stm32mp;protocol=https"
## tag: v6.6-stm32mp-r2
SRCREV = "f01241fbba4d879fa770685629b49d42e904e43c"
#SRCREV = "${AUTOREV}"
PV := "6.6.78"
PR := "r2"
## repo branch to use
KBRANCH = "v6.6-stm32mp"
LINUX_VERSION = "6.6.78"
LINUX_VERSION_EXTENSION = "-stm32mp"
## ================================================================
## KERNEL CONFIG
## ================================================================
## arm64 doesnt use "zImage"
KERNEL_IMAGETYPE = "Image"
## by default defconfig is searched inside "arch/<ARCH>/configs/" dir
## KBUILD_DEFCONFIG is a relative path from this dir
KBUILD_DEFCONFIG = "defconfig"
##
## or can be supplied via SRC_URI
#FILESEXTRAPATHS:prepend := "${THISDIR}/defconfig:"
#SRC_URI += "file://defconfig;subdir=git"
## ================================================================
## MACHINE
## ================================================================
## by default dt are searched inside "arch/<ARCH>/boot/dts/" dir
## KERNEL_DEVICETREE is a relative path from this dir
## and must end with .dtb extension
KERNEL_DEVICETREE = "st/stm32mp257f-dk.dtb"e che produce i seguenti file che ci serviranno per fare il boot:
tmp/deploy/images/stm32mp257f-dk
├── Image
├── stm32mp257f-dk.dtb
└── ...primo boot
adesso procediamo con il boot,
quindi per prima cosa dobbiamo
posizionare i file Image e dtb
all’interno della sdcard.
Non esistono vincoli su posizione e label
della partizione, ma basta
che questa abbia un filesystem riconoscibile
da uboot e da linux.
Creiamo quindi una partizione formattata con ext4:
DEVICE="${1:-}"
IMAGE="${2:-}"
DTB="${3:-}"
# Linux filesystem data (ext2/ext4/fat file system)
# TYPE CODE GUID = 0FC63DAF-8483-4772-8E79-3D69D8477DE4
PART=10
sudo sgdisk \
-n ${PART}:0:+100M \
-c ${PART}:bootfs \
-t ${PART}:0FC63DAF-8483-4772-8E79-3D69D8477DE4 \
-u ${PART}:R \
"$DEVICE"
sudo mkfs.ext4 -L "bootfs" "${DEVICE}${PART}"
# CONTENTS
MNTPOINT=/tmp/bootfs
sudo mkdir -p "$MNTPOINT"
sudo mount "${DEVICE}${PART}" "$MNTPOINT"
sudo install -m 644 "$IMAGE" "$MNTPOINT/Image"
sudo install -m 644 "$DTB" "$MNTPOINT/dtb"
sudo sync
sudo umount "$MNTPOINT"adesso colleghiamo la scheda e avviamo da sdcard. Arrivati alla console uboot esaminiamo la sdcard:
STM32MP> mmc list
STM32 SD/MMC: 0 (SD)
STM32 SD/MMC: 1 (eMMC)
STM32MP> mmc part
Partition Map for MMC device 0 -- Partition Type: EFI
Part Start LBA End LBA Name
Attributes
Type GUID
Partition GUID
1 0x00000800 0x00000bff "fsbla"
attrs: 0x0000000000000000
type: 8da63339-0007-60c0-c436-083ac8230908
(8da63339-0007-60c0-c436-083ac8230908)
guid: 3181429f-6c93-4500-bd4d-264f0af4fd4d
2 0x00001000 0x00002fff "fip"
attrs: 0x0000000000000000
type: 19d5df83-11b0-457b-be2c-7559c13142a5
(19d5df83-11b0-457b-be2c-7559c13142a5)
guid: 4db28b33-fe10-4c2b-93be-4b79de8be706
10 0x00003000 0x00034fff "bootfs"
attrs: 0x0000000000000000
type: 0fc63daf-8483-4772-8e79-3d69d8477de4
(linux)
guid: 243f52c4-209b-4cf3-a136-147882bcb87fla sdcard è nello slot mmc 0
e la partizione bootfs ha indice 10
che in esadecimale corrisponde a A.
Siccome uboot utilizza il formato esadecimale
per tutti i numeri nei comandi, facciamo un po’ di attenzione.
Verifichiamo la presenza di un filesystem nella partizione A
e esploriamone i contenuti:
STM32MP> fstype mmc 0:A
ext4
STM32MP> ls mmc 0:A /
<DIR> 1024 .
<DIR> 1024 ..
25053192 Image
88749 dtb
STM32MP> size mmc 0:A /Image
STM32MP> print filesize
filesize=17e4808
STM32MP> size mmc 0:A /dtb
STM32MP> print filesize
filesize=15aad il comando size assegna alla variabile filesize
il valore esadecimale in byte della dimensione del file.
Ok, quindi tutto a posto e procediamo col boot.
Prima dobbiamo caricare kernel e dtb
da disco a RAM e poi fornire a booti
gli address in ram di questi binari.
Per decidere gli indirizzi possiamo
utilizzare le variabili standard di uboot
kernel_addr_r e fdt_addr_r
come indirizzi di memoria.
Una altra variabile comoda è bootargs
che definisce gli argomenti
da passare al kernel.
Vabbe dai proviamo:
STM32MP> load mmc 0:A ${kernel_addr_r} Image
17770504 bytes read in 943 ms (18 MiB/s)
STM32MP> load mmc 0:A ${fdt_addr_r} dtb
117096 bytes read in 215 ms (531.3 KiB/s)
STM32MP> setenv bootargs "earlycon ignore_loglevel"
STM32MP> booti ${kernel_addr_r} - ${fdt_addr_r}utilizzando dei bootargs che indichino
al kernel di stampare il log,
all’avvio vedremo la console riempirsi di messaggi:
## Flattened Device Tree blob at 90000000
Booting using the fdt blob at 0x90000000
Working FDT set to 90000000
Loading Device Tree to 000000008ffe0000, end 000000008ffff967 ... OK
Working FDT set to 8ffe0000
Starting kernel ...
[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x411fd040]
[ 0.000000] Linux version 6.6.78-stm32mp (oe-user@oe-host) (aarch64-oe-linux-gcc (GCC) 13.4.0, GNU ld (GNU Binutils) 2.42.0.20240723) #1 SMP PREEMPT Wed Jun 25 12:03:32 UTC 2025
[ 0.000000] KASLR disabled due to lack of seed
[ 0.000000] Machine model: STMicroelectronics STM32MP257F-DK Discovery Board
[ 0.000000] Reserved memory: created CMA memory pool at 0x00000000f2200000, size 128 MiBil log finisce quando avviene lo switch da bootconsole a console:
[ 0.012702] printk: console [tty0] enabled
[ 0.016675] printk: bootconsole [stm32] disabledquindi ai bootargs aggiungiamo la definizione
della console seriale che viene inizializzata da linux:
setenv bootargs "console=ttySTM0,115200 earlycon ignore_loglevel"questo lo possiamo trovare dai documenti STM
oppure dal fatto che nel devicetree viene
definito stdout-path = "serial0:115200n8"
dove serial0 usa il driver st,stm32h7-uart
che registra le console seriali in linux con il prefisso ttySTM.
La spiegazione è complicata,
ma per il momento fidiamoci
e poi la vedremo in dettaglio.
A conferma di questo, possiamo trovare questo esatto prefisso
nei driver definiti nel kernel:
#define STM32_SERIAL_NAME "ttySTM"riavviando la scheda, ridefinendo i bootargs e ripetendo il processo, arriviamo ad un kernel panic perchè non è presente un root filesystem:
[ 2.918219] /dev/root: Can't open blockdev
[ 2.918269] VFS: Cannot open root device "" or unknown-block(0,0): error -6
[ 2.923726] Please append a correct "root=" boot option; here are the available partitions:
[ 2.932190] List of all bdev filesystems:
[ 2.936125] ext3
[ 2.936129] ext4
[ 2.938055] ext2
[ 2.939973] vfat
[ 2.941898]
[ 2.945330] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
[ 2.953683] CPU: 1 PID: 1 Comm: swapper/0 Not tainted 6.6.78-stm32mp #1
[ 2.960328] Hardware name: STMicroelectronics STM32MP257F-DK Discovery Board (DT)
[ 2.967875] Call trace:
[ 2.970296] dump_backtrace+0xa0/0x104
[ 2.974036] show_stack+0x14/0x1c
[ 2.977367] dump_stack_lvl+0x44/0x58
[ 2.981101] dump_stack+0x14/0x1c
[ 2.984431] panic+0x2f0/0x34c
[ 2.987461] mount_root_generic+0x228/0x2f4
[ 2.991598] mount_root+0x194/0x25c
[ 2.995130] prepare_namespace+0x54/0x26c
[ 2.999164] kernel_init_freeable+0x228/0x280
[ 3.003499] kernel_init+0x20/0x1d4
[ 3.007033] ret_from_fork+0x10/0x20
[ 3.010665] SMP: stopping secondary CPUs
[ 3.014597] Kernel Offset: disabled
[ 3.018122] CPU features: 0x0,00000000,00000000,0000401b
[ 3.023459] Memory Limit: none
[ 3.026487] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---Bello. Almeno il kernel qualcosa fa dai.
rootfs in ram / initramfs
lo step finale sarà quello di arrivare ad un rootfs salvato in una partizione su disco, ma per il momento utilizziamo un initramfs, ovvero un root-filesystem che vive su disco, ma viene caricato in ram e poi non salvato. Questo perchè non abbiamo ancora configurato il kernel e dobbiamo prima verificare di riuscire ad interagire correttamente con l’ hardware. In questo modo, invece, sfruttiamo il fatto che uboot è già in grado di lavorare con la sdcard e quindi sarà lui ad effettuare l’operazione di caricare initramfs in ram.
Per generare un initramfs possiamo utilizzare
il recipe di Yocto core-image-minimal-initramfs:
bitbake core-image-minimal-initramfsnel deploy troveremo il initramfs compresso:
tmp/deploy/images/stm32mp257f-dk
├── core-image-minimal-initramfs-stm32mp257f-dk.cpio.gz
└── ...che per poter essere decompresso e usato come filesystem
necessita che il kernel sia compilato con delle opzioni specifiche.
Questo lo vedremo dopo, quindi per il momento
aggiungiamo in local.conf la
definizione del tipo di initramfs che vogliamo:
INITRAMFS_FSTYPES = "cpio"ribuildando il recipe otterremo:
tmp/deploy/images/stm32mp257f-dk
├── core-image-minimal-initramfs-stm32mp257f-dk.cpio
└── ...copiamo il file nella stessa partizione bootfs che avevamo già creato e da uboot eseguiamo:
STM32MP> load mmc 0:A ${kernel_addr_r} Image
25053192 bytes read in 1248 ms (19.1 MiB/s)
STM32MP> load mmc 0:A ${fdt_addr_r} dtb
117096 bytes read in 215 ms (531.3 KiB/s)
STM32MP> load mmc 0:A ${ramdisk_addr_r} initramfs.cpio
32108544 bytes read in 1550 ms (19.8 MiB/s)
STM32MP> setenv bootargs "console=ttySTM0,115200 earlycon ignore_loglevel"
STM32MP> booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}arriveremo a un punto tipo:
[ 1.386586] ------------[ cut here ]------------
[ 1.390816] WARNING: CPU: 1 PID: 1 at /drivers/firmware/arm_scmi/driver.c:3008 scmi_driver_init+0x0/0xc
[ 1.400260] Modules linked in:
[ 1.403324] CPU: 1 PID: 1 Comm: swapper/0 Not tainted 6.6.78-stm32mp #1
[ 1.409968] Hardware name: STMicroelectronics STM32MP257F-DK Discovery Board (DT)
[ 1.417490] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 1.424486] pc : scmi_driver_init+0x0/0xc
[ 1.428508] lr : do_one_initcall+0x68/0x180
[ 1.432704] sp : ffff0000800c7dc0
[ 1.436028] x29: ffff0000800c7dc0 x28: 0000000000000000 x27: ffff800081066068
[ 1.443198] x26: ffff800080fe00a0 x25: ffff800080f6edd0 x24: ffff800080fc6c90
[ 1.450367] x23: ffff800081066078 x22: 0000000000000000 x21: ffff000080098000
[ 1.457534] x20: ffff8000817ec000 x19: ffff80008102720c x18: ffff0000800c7cf0
[ 1.464702] x17: ffff80008172e9e0 x16: 0000000035eab635 x15: 0000000009f6f035
[ 1.471869] x14: 000000007b1a73ba x13: 0000000026ecf358 x12: 00000000335b73b7
[ 1.479036] x11: 000000008c1ee96f x10: 00000000b92e1044 x9 : 0000000000000000
[ 1.486204] x8 : ffff0000800c7dc0 x7 : 0000000000000000 x6 : 000000000000003f
[ 1.493375] x5 : 0000000000000040 x4 : ffffffffffffffd0 x3 : 0000000000000280
[ 1.500541] x2 : 0000000000000000 x1 : 0000000000000000 x0 : 0000000000000000
[ 1.507711] Call trace:
[ 1.510162] scmi_driver_init+0x0/0xc
[ 1.513837] kernel_init_freeable+0x1c0/0x280
[ 1.518212] kernel_init+0x20/0x1d0
[ 1.521713] ret_from_fork+0x10/0x20
[ 1.525302] ---[ end trace 0000000000000000 ]---
...
[ 1.584679] registered taskstats version 1
[ 6.676016] request_module: modprobe binfmt-464c cannot be processed, kmod busy with 50 threads for more than 5 seconds now
[ 11.796022] request_module: modprobe binfmt-464c cannot be processed, kmod busy with 50 threads for more than 5 seconds now
[ 16.916008] request_module: modprobe binfmt-464c cannot be processed, kmod busy with 50 threads for more than 5 seconds now
[ 22.036012] request_module: modprobe binfmt-464c cannot be processed, kmod busy with 50 threads for more than 5 seconds now
...
[ 22.102891] Warning: unable to open an initial console.
[ 22.110430] Freeing unused kernel memory: 5312K
[ 22.112768] Run /init as init process
[ 22.116315] with arguments:
[ 22.119275] /init
[ 22.121555] with environment:
[ 22.124701] HOME=/
[ 22.127052] TERM=linux
[ 22.129989] Failed to execute /init (error -2)
...
[ 27.412013] request_module: modprobe binfmt-464c cannot be processed, kmod busy with 50 threads for more than 5 seconds now
[ 27.415350] Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance.
...
E/TC:1 Panic 'Watchdog' at /usr/src/debug/optee-os/4.0.0-stm32mp/core/drivers/stm32_iwdg.c:218 <stm32_iwdg_it_handler>
E/TC:1 TEE load address @ 0x82000000
E/TC:1 Call stack:
E/TC:1 0x82007ef0
E/TC:1 0x82043428
E/TC:1 0x8201c110
E/TC:1 0x82040f54
E/TC:1 0x820141e0Da qui vediamo una serie di errori:
/drivers/firmware/arm_scmi/driver.cnon riesce a fare initmodprobe binfmt-464c cannot be processedquindi il kernel modulebinfmt-464cnon viene caricatoNo working init foundquindi il kernel non riesce a eseguireinitdel initramfsPanic 'Watchdog'quindi il watchdog protetto da OPTEE scade perchè il kernel non lo riavvia mai
Inoltre anche nel log di avvio di uboot vediamo dei riferimenti a questo watchdog:
WDT: Started watchdog with servicing every 1000ms (32s timeout)Adesso iniziano i grandi cazzi. Meglio dedicargli un articolo intero.