# Running Linux 6.14 on stm32f429
Contributed by < [`kk908676`]((https://github.com/kk908676)) >
Continued from @cychen [`hugo0406`](https://github.com/hugo0406) :
[Linux 核心專題: Running Linux 6.10 on stm32f429](https://hackmd.io/0Ua7E28UTDOR8vo0Cqikdg?view)
## STM32F429i-Discovery
- STM32F429 MCU (ARM Cortex-M4 up to 180MHz, 2MB internal flash, 256KB internal RAM)
- 8MB SDRAM
- USB OTG Full-Speed
- 2.4" LCD
- MEMS motion sensor
- STLink/V2 (debugging)
- Extension headers

### STM32 Reset
參考 STM32F429 的 [Reference Manual](https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf)
Boot mode selection pins: BOOT0,BOOT1
CPU reset 後會從這兩根 pin 的狀態來決定哪一塊 memory 當 boot space,STM32F429 選用的 default boot space 是 Main Flash memory
| BOOT1 | BOOT0 | Boot Mode | Aliasing |
|:-----:|:-----:|:----------------- |:-------- |
| X | 0 | Main Flash Memory |Main Flash Memory is selected as the boot space |
| 0 | 1 | System Memory |System Memory is selected as the boot space |
| 1 | 1 | Embedded SRAM |Embedded SRAM is selected as the boot space |
Flash memory 的 start address 是 `0x08000000` 等開機時,會自動將 `0x08000000-0x080FFFFF` 映射到 `0x00000000-0x000FFFFF`,所以將 bin檔燒在 `0x08000000` ,STM32F429 就可以從 `0x00000000` 開始執行

參考 STM32F429 的 [Reference Manual](https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf)
- ARM Cross Compile Toolchain:
```bash
sudo apt-get install arm-linux-gnueabi-gcc
which arm-linux-gnueabi-gcc
export PATH=/usr/bin:$PATH
source ~/.bashrc
```
```diff
- sudo apt-get install arm-linux-gnueabi-gcc
+ sudo apt install gcc-arm-linux-gnueabi
```
- STLINK Tools: `stlink` is an open source toolset to program and debug STM32 devices and boards manufactured by STMicroelectronics.
```bash
cd ~
git clone https://github.com/stlink-org/stlink
cd stlink
sudo apt-get install -y cmake libusb-1.0-0-dev
sudo apt-get -y install cmake
sudo apt-get install libstlink1
make
cd build/Release
sudo make install
```
To verify the successful installation
```bash
$ st-flash
invalid command line
usage: st-flash [options] read [file] [addr] [size]
st-flash [options] write <file> [addr] [size]
st-flash [options] write <value>
st-flash [options] erase <addr> <size>
st-flash [options] reset
options:
--freq <kHz> Frequency of JTAG/SWD, default 1800kHz.
--serial <serial> STLink device to use.
--connect-under-reset Pull reset low while connecting.
--hot-plug Connect without reset.
--reset Reset after writing.
--format {binary|ihex} Format of file to read or write. When writing
with ihex specifying addr is not needed.
--flash <size> Specify size of flash, e.g. 128k, 1M.
--area <area> Area to access, one of: main(default), system,
otp, option, option_boot_add, optcr, optcr1.
--opt Skip writing empty bytes at the tail end.
--debug Output extra debug information.
--version Print version information.
--help Show this help.
examples:
st-flash --area=option read [file] [size]
st-flash --area=option write 0xXXXXXXXX
st-flash --area=option_boot_add read
st-flash --area=option_boot_add write 0xXXXXXXXX
st-flash --area=optcr read
st-flash --area=optcr write 0xXXXXXXXX
st-flash --area=optcr1 read
st-flash --area=optcr1 write 0xXXXXXXXX
st-flash --area=otp read <file>
st-flash --area=otp write <file> 0xXXXXXXXX
```
這時候可能會發現有這麼一段錯誤訊息
```
$ st-info --probe
2025-03-11T21:19:20 ERROR usb.c: Could not open USB device 0x0483:0x374b, access error.
```
大多數發行版不允許存取 USB 裝置,從[官方檔案](https://github.com/stlink-org/stlink/blob/testing/doc/compiling.md#Windows)中我們可以解決這問題
```
Set device access permissions and the role of udev
By default most distributions don't allow access to USB devices. In this context udev rules, which create devices nodes, are necessary to run the tools without root permissions. To achieve this you need to ensure that the group plugdev exists and the user who is trying to access these devices is a member of this group.
Within the sourcefolder of the project, these rules are located in the subdirectory config/udev/rules.d and are automatically installed along with sudo make install on linux. Afterwards it may be necessary to reload the udev rules:
$ sudo cp -a config/udev/rules.d/* /lib/udev/rules.d/
$ sudo udevadm control --reload-rules
$ sudo udevadm trigger
udev will now create device node files, e.g. /dev/stlinkv3_XX, /dev/stlinkv2_XX, /dev/stlinkv1_XX.
```
如此一來就順利讀取到我們想要的開發板了
```
$ st-info --probe
Found 1 stlink programmers
version: V2J17
serial: 303030303030303030303031
flash: 2097152 (pagesize: 16384)
sram: 262144
chipid: 0x419
dev-type: STM32F42x_F43x
```
### 燒錄測試
可透過下方連結取得完整程式碼
```bash
git clone https://gist.github.com/kk908676/1128f48c35bb4b89933ba319b4a282e2
```
- [ ] `blink.c`
```c
#define RCC_AHB1_PERI_ENBLR_ADDR (*((volatile unsigned long*) (0x40023800 + 0x30) ))
#define GPIO_G_PORTMODE (*((volatile unsigned long*) 0x40021800 ))
#define GPIO_G_OUTPUT_TYPE (*((volatile unsigned long*) (0x40021800 + 0x4 ) ))
#define GPIO_G_PUPD (*((volatile unsigned long*) (0x40021800 + 0xC ) ))
#define GPIO_G_PORT_SETRESET (*((volatile unsigned long*) (0x40021800 + 0x18) ))
asm(".word 0x20001000");
asm(".word main ");
int main()
{
int i;
RCC_AHB1_PERI_ENBLR_ADDR |= (0x00000001 << 6); /*Enalbe GPIO G clock*/
GPIO_G_PORTMODE |= (0x00000001 << 26 | 0x00000001 << 28); /* Set PG13 PG14 mode to GPIO */
GPIO_G_PORTMODE &= ~(0x00000001 << 27 | 0x00000001 << 29);
GPIO_G_OUTPUT_TYPE &= ~(0x00000001 << 13 | 0x00000001 << 14); /*Set PG13 PG14 to push-pull*/
GPIO_G_PUPD |= (0x00000001 << 27 | 0x00000001 << 29); /*Set PG13 PG14 to pull-down*/
while(1)
{
GPIO_G_PORT_SETRESET = 0x20004000; /*PG13 off, PG14 on*/
for(i = 0 ; i < 100000 ; i++);
GPIO_G_PORT_SETRESET = 0x40002000; /*PG14 off, PG13 on*/
for(i = 0 ; i < 100000 ; i++);
}
return 0;
}
```
- [ ] `Makefile`
```shell
CROSS_COMPILE ?= arm-none-eabi-
.PHONY: all
all: blink.bin
blink.o: blink.c
$(CROSS_COMPILE)gcc -mcpu=cortex-m4 -mthumb -nostartfiles -c blink.c -o blink.o
blink.out: blink.o blink.ld
$(CROSS_COMPILE)ld -T blink.ld -o blink.out blink.o
blink.bin: blink.out
$(CROSS_COMPILE)objcopy -j .text -O binary blink.out blink.bin
clean:
rm -rf *.o *.out *.bin
```
- [ ] `blink.ld`
```c
SECTIONS
{
. = 0x0;
.text :
{
*(.text)
}
}
```
編譯完後會產生 `blink.out` 及 `blink.bin` ,將 `blink.bin` 燒進 flash
```
$ st-flash --reset write blink.bin 0x08000000
st-flash 1.8.0
2025-03-13T16:45:58 INFO common_legacy.c: STM32F4x5_F4x7: 192 KiB SRAM, 1024 KiB flash in at least 16 KiB pages.
file blink.bin md5 checksum: 4bb06e5d434b414b47c0f421f60e795, stlink checksum: 0x00002cae
2025-03-13T16:45:58 INFO common_flash.c: Attempting to write 160 (0xa0) bytes to stm32 address: 134217728 (0x8000000)
2025-03-13T16:45:58 INFO flash_loader.c: Starting Flash write for F2/F4/F7/L4
2025-03-13T16:45:58 INFO flash_loader.c: Successfully loaded flash loader in sram
2025-03-13T16:45:58 INFO flash_loader.c: Clear DFSR
2025-03-13T16:45:58 INFO flash_loader.c: Clear CFSR
2025-03-13T16:45:58 INFO flash_loader.c: Clear HFSR
2025-03-13T16:45:58 INFO flash_loader.c: enabling 32-bit flash writes
2025-03-13T16:45:58 INFO common_flash.c: Starting verification of write complete
2025-03-13T16:45:58 INFO common_flash.c: Flash written and verified! jolly good!
```
燒錄後按 RESET 即可看到 `LD3(綠)`、`LD4(紅)` 開始交替閃爍
燒進 flash 可能會遇到寫入保護( Write Protection )鎖定 , 因此要先解除保護:
```
$ st-flash erase
```
之後再按照上方操作即可
### Boot loader
當處理器啟動時,記憶體中尚未載入作業系統,因此需要一段特殊的軟體來將作業系統從儲存媒體中載入至記憶體。這段軟體通常是一小段稱為「開機載入器(boot loader)」的程式碼。
在現代電腦系統中,開機載入器通常與UEFI(統一可延伸韌體介面)合作運作,並從使用GUID分割表(GPT, GUID Partition Table)的磁碟架構中讀取作業系統的相關資料。與傳統BIOS與MBR(主開機記錄區)架構不同,UEFI與GPT提供了更大的磁碟支援、更高的可靠性,以及更彈性的系統啟動流程
#### U-Boot
是一個常用於嵌入式系統的開機載入器(bootloader)
- 啟動時,U-Boot 負責:
1. 初始化 CPU 和記憶體(如 DDR/SDRAM)
2. 設定時鐘、中斷控制器、UART 等周邊
3. 載入作業系統(如 Linux kernel)到記憶體
4. 傳遞啟動參數(例如 device tree 與 initrd)給 kernel
- 可透過 串列埠提供 U-Boot Shell,進行手動載入或除錯
- 支援多種啟動來源(如 NOR/NAND Flash、SD 卡、SPI Flash、USB、TFTP 等)
下載 U-boot 原始碼:
```bash
git clone git://git.denx.de/u-boot.git
cd u-boot
```
編譯 U-boot :
```bash=
make mrproper
make stm32f429-discovery_defconfig
# make menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j12
```
- 第一行是移除之前編譯所產生的文件、配置、備份
- 第二行是生成一個 .config 預設配置
- 第三行是用於更改預設配置,因此它會修改產生的 .config
- 第四行直接開始編譯 U-Boot 原始碼,目標檔案是 u-boot.bin
燒進 FLASH
```bash
$ st-flash write u-boot.bin 0x08000000
st-flash 1.8.0-99-gb149e24
2025-05-04T15:53:54 INFO common_legacy.c: STM32F42x_F43x: 256 KiB SRAM, 2048 KiB flash in at least 16 KiB pages.
file u-boot.bin md5 checksum: d14f49e335e81f6078882ae9ed546dac, stlink checksum: 0x00d07b04
2025-05-04T15:53:54 INFO common_flash.c: Attempting to write 169436 (0x295dc) bytes to stm32 address: 134217728 (0x8000000)
EraseFlash - Sector:0x0 Size:0x4000 -> Flash page at 0x8000000 erased (size: 0x4000)
EraseFlash - Sector:0x1 Size:0x4000 -> Flash page at 0x8004000 erased (size: 0x4000)
EraseFlash - Sector:0x2 Size:0x4000 -> Flash page at 0x8008000 erased (size: 0x4000)
EraseFlash - Sector:0x3 Size:0x4000 -> Flash page at 0x800c000 erased (size: 0x4000)
EraseFlash - Sector:0x4 Size:0x10000 -> Flash page at 0x8010000 erased (size: 0x10000)
EraseFlash - Sector:0x5 Size:0x20000 -> Flash page at 0x8020000 erased (size: 0x20000)
2025-05-04T15:53:59 INFO flash_loader.c: Starting Flash write for F2/F4/F7/L4
2025-05-04T15:53:59 INFO flash_loader.c: Successfully loaded flash loader in sram
2025-05-04T15:53:59 INFO flash_loader.c: Clear DFSR
2025-05-04T15:53:59 INFO flash_loader.c: Clear CFSR
2025-05-04T15:53:59 INFO flash_loader.c: Clear HFSR
2025-05-04T15:53:59 INFO flash_loader.c: enabling 32-bit flash writes
2025-05-04T15:54:01 INFO common_flash.c: Starting verification of write complete
2025-05-04T15:54:03 INFO common_flash.c: Flash written and verified! jolly good!
2025-05-04T15:54:03 INFO common_legacy.c: Go to Thumb mode
```
#### USART Connection
- The STM32F429 Discovery is equipped with various USARTs. USART stands for Universal Synchronous Asynchronous Receiver Transmitter. The USARTs on the STM32F429 support a wide range of serial protocols, the usual asynchronous ones, plus things like IrDA, SPI etc. Since the STM32 works on 3.3V levels, a level shifting component is needed to connect the USART of the STM32F429 to a PC serial port.
| STM32 PIN | VCP |
| --------- | ---- |
| PA9 | USART1_TX |
| PA10 | USART1_RX |
USB to TTL 接線:
- PA9 $\to$ RXD
- PA10 $\to$ TXD
- GND $\to$ GND

```shell
$ sudo dmesg | grep tty
[ 0.222738] printk: legacy console [tty0] enabled
[18797.223501] usb 1-11: pl2303 converter now attached to ttyUSB0
$ sudo chmod 666 /dev/ttyUSB0
```
安裝 `kermit`
```
$ sudo apt-get install ckermit
```
可以從 [官方文件中](https://github.com/u-boot/u-boot/blob/master/drivers/serial/Kconfig) 得知 **Baud Rate** 預設為 `115200` ,**UART number** 預設為 `1`
```
config BAUDRATE
int "Default baudrate"
default 115200
help
Select a default baudrate, where "default" has a driver-specific
meaning of either setting the baudrate for the early debug UART
in the SPL stage (most drivers) or for choosing a default baudrate
in the absence of an environment setting (serial_mxc.c).
config CONS_INDEX
int "UART used for console"
depends on SPECIFY_CONSOLE_INDEX
range 0 6
default 1
help
Set this to match the UART number of the serial console.
```
設定與開發板溝通的基本參數
輸入 `vi ~/.kermrc`
```
set line /dev/ttyUSB0
set speed 115200
set carrier-watch off
set handshake none
set flow-control none
robust
set file type binary
set file name literal
set receive packet-length 1000
set send packet-length 1000
set window 5
```
接著輸入 `kermit` 就可以看到
```
$ kermit
C-Kermit 10.0 pre-Beta.11, 06 Feb 2024, for Linux+SSL (64-bit)
Copyright (C) 1985, 2024,
Trustees of Columbia University in the City of New York.
Open Source 3-clause BSD license since 2011.
Type ? or HELP for help.
(~/Desktop/) C-Kermit>
```
因為剛剛已經設定好了一些基本的參數,這時只要在命令列輸入 `c` 且按下開發板上的 RESET 就可以看到
```
(~/Desktop/) C-Kermit>c
Connecting to /dev/ttyUSB1, speed 115200
Escape character: Ctrl-\ (ASCII 28, FS): enabled
Type the escape character followed by C to get back,
or followed by ? to see other options.
----------------------------------------------------
U-Boot 2025.04-rc4-00008-g15d6518c942f (Mar 17 2025 - 16:28:20 +0800)
DRAM: 8 MiB
Core: 73 devices, 11 uclasses, devicetree: separate
Flash: 2 MiB
Loading Environment from Flash... *** Warning - bad CRC, using default environment
In: serial@40011000
Out: serial@40011000
Err: serial@40011000
U-Boot > help
? - alias for 'help'
base - print or set address offset
bdinfo - print Board Info structure
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
bootelf - Boot from an ELF image in memory
bootm - boot application image from memory
bootvx - Boot vxWorks from an ELF image
cmp - memory compare
coninfo - print console devices and information
cp - memory copy
crc32 - checksum calculation
dm - Driver model low level access
echo - echo args to console
editenv - edit environment variable
env - environment handling commands
erase - erase FLASH memory
exit - exit script
false - do nothing, unsuccessfully
fdt - flattened device tree utility commands
flinfo - print FLASH memory information
go - start application at address 'addr'
help - print command description/usage
iminfo - print header information for application image
imls - list all images found in flash
imxtract - extract a part of a multi-image
itest - return true/false on integer compare
loadb - load binary file over serial line (kermit mode)
loads - load S-Record file over serial line
loadx - load binary file over serial line (xmodem mode)
loady - load binary file over serial line (ymodem mode)
loop - infinite loop on address range
md - memory display
mm - memory modify (auto-incrementing address)
mw - memory write (fill)
nm - memory modify (constant address)
panic - Panic with optional message
pinmux - show pin-controller muxing
printenv - print environment variables
protect - enable or disable FLASH write protection
reset - Perform RESET of the CPU
run - run commands in an environment variable
saveenv - save environment variables to persistent storage
setenv - set environment variables
showvar - print local hushshell variables
sleep - delay execution for some time
source - run script from memory
test - minimal test like /bin/sh
timer - access the system timer
true - do nothing, successfully
version - print monitor, compiler and linker version
U-Boot >
```
`version` -> 顯示目前 U-Boot 版本
```
U-Boot > version
U-Boot 2024.07-rc3-00020-gea722aa5eb (Jun 09 2024 - 22:25:14 +0800)
```
`bdinfo` -> 顯示目前板子資訊
```
U-Boot > bdinfo
boot_params = 0x00000000
DRAM bank = 0x00000000
-> start = 0x90000000
-> size = 0x00800000
flashstart = 0x08000000
flashsize = 0x00200000
flashoffset = 0x000241b8
baudrate = 115200 bps
relocaddr = 0x907ca000
reloc off = 0x887ca000
Build = 32-bit
fdt_blob = 0x905c2cb0
new_fdt = 0x905c2cb0
fdt_size = 0x00005220
lmb_dump_all:
memory.cnt = 0x1 / max = 0x10
memory[0] [0x90000000-0x907fffff], 0x00800000 bytes flags: 0
reserved.cnt = 0x1 / max = 0x10
reserved[0] [0x905be9a8-0x907fffff], 0x00241658 bytes flags: 0
devicetree = separate
arch_number = 0x00000000
TLB addr = 0x907f0000
irq_sp = 0x905c2ca0
sp start = 0x905c2c90
Early malloc usage: f40 / 2000
```
### Kernel 核心編譯
Linux-6.14.0
```bash
git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git -b linux-6.14.y --depth 1
```
#### Menuconfig & Kconfig
詳細內容請參閱核心程式碼目錄下的檔案 ./Documentation/kbuild/kconfig-language.rst
使用預設 `.config` 檔
```
cd linux/
make ARCH=arm stm32_defconfig
```
#### Menu Config
用來對剛剛已經產生的 `.config` 再做一些調整
```bash
make ARCH=arm menuconfig
```
先檢查是否有安裝 `libncurses5-dev`
```
sudo apt-get install libncurses5-dev
```
之後可以看到以下畫面

#### Initramfs
##### 方法一(較舊)
他是在啟動階段被 `Linux` 核心呼叫的臨時檔案系統,用於根目錄被掛載之前的準備工作, 這裡用 [Embedded Linux Wiki](https://elinux.org/File:Stm32_mini_rootfs.cpio.bz2#filelinks) 所提供的 `Stm32 mini rootfs.cpio.bz2`
下載之後先解壓縮成 `cpio` 檔 :
```
bzip2 -d Stm32_mini_rootfs.cpio.bz2
```
接下來建立 `rootfs` 目錄,作為之後 `Linux` 啟動時的臨時檔案系統:
```
mkdir rootfs
cd rootfs
```
將下載的檔案解壓縮到 `rootfs` 中
```
cpio -idmv < ../Stm32_mini_rootfs.cpio
```
確認 `dev` 中是否有
`null` : 特殊裝置,所有寫入的資料都會被丟棄,用於丟棄輸出或忽略訊息
`tty0` : 目前活動中的虛擬主控台( `active virtual console` ),系統訊息會顯示在目前啟用的終端上
`console` : 系統的核心輸出通道,用於顯示開機訊息與核心錯誤,會對應到具體的終端裝置(如 `ttyS0` 或 `tty0` )
如果沒有就需要手動加入
```
sudo mknod console c 5 1
sudo mknod null c 1 3
sudo mknod tty0 c 204 64
```
最後在 `rootfs` 新增 `init` 檔案,用來手動掛載 `/dev` 目錄並初始化裝置節點,然後將標準輸入、輸出和錯誤輸出重定向到終端機,最後執行系統的初始化程式 `/sbin/init`
```bash
#!/bin/sh
# devtmpfs does not get automounted for initramfs
/bin/mount -t devtmpfs devtmpfs /dev
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
exec /sbin/init $
```
最後再將此 `rootfs` 壓縮成 `rootfs.cpio`
```
find . | cpio -o -H newc > ../rootfs.cpio
```
##### 方法二(較新)
下載 [Linux on the STM32F429I Discovery board with Buildroot](https://github.com/winfred-lu/STM32F429I-disco_Buildroot) 專案
```bash
git clone https://github.com/winfred-lu/STM32F429I-disco_Buildroot.git
```
接著依序執行以下命令
```
make bootstrap
make defconfig
make build
```
執行完後在 `buildroot/output/images/` 看到我們需要的 `rootfs.cpio`
`rootfs` 準備好了之後要在 `menuconfig` 設定 `rootfs` 的來源位置, 這樣核心可以在啟動時加載它作為初始 `rootfs`
位置 : General setup $\to$ Initial RAM filesystem and RAM disk (initramfs/initrd) support
```bash
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
(/home/an/Downloads/rootfs.cpio) Initramfs source file(s)
```

編譯核心 :
```
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j12
```
編譯 `Linux` 核心並產生可啟動的核心映像 `uImage` :
(你會在 `linux/arch/arm/boot/` 看到 `uImage` , `uImage` 是 `U-Boot` 用來引導核心的映像格式)
```
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- uImage LOADADDR=0x90008000 -j12
CALL scripts/checksyscalls.sh
Kernel configured for XIP (CONFIG_XIP_KERNEL=y)
Only the xipImage target is available in this case
make[2]: *** [arch/arm/boot/Makefile:45: arch/arm/boot/Image] Error 1
make[1]: *** [arch/arm/Makefile:300: Image] Error 2
make: *** [Makefile:251: __sub-make] Error 2
```
因為預設的 kernel 設定中啟用了 XIP (Execute In Place) 模式,導致無法產生一般的 uImage, 只能建立 xipImage, 所以需要關閉 XIP 設定
位置 : Boot options $\to$ Kernel Execute-In-Place from ROM

如畫面中關閉之後再執行
```
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- uImage LOADADDR=0x90008000 -j12
```
:::warning
TODO: 比照 [stm32f429-linux-builder](https://github.com/jserv/stm32f429-linux-builder/blob/master/Makefile),藉由 GNU make (或其他建構工具),將上述操作予以自動化。
:::
編譯裝置樹二進制檔案 :
(你會在 `linux/arch/arm/boot/dts/st/` 看到 `stm32f429-disco.dtb` 裝置樹描述了周邊硬體的組成方式,並告訴核心如何操作這些裝置,因此在編譯核心時需要產生對應的裝置樹檔案,這樣核心才能正確識別並配置周邊硬體)
```
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- dtbs
```
等待這幾個核心檔案都產生之後, 接下來就是放入到 `SDRAM` 之後核心才能做後續相對應的硬體和裝置的初始化
從剛剛 `U-Boot` 所產生的資訊來看, `SDRAM` 的起始位置是 `0x90000000` 大小是 `8MB`
```
U-Boot > bdinfo
boot_params = 0x00000000
DRAM bank = 0x00000000
-> start = 0x90000000
-> size = 0x00800000
flashstart = 0x08000000
flashsize = 0x00200000
flashoffset = 0x000241b8
baudrate = 115200 bps
relocaddr = 0x907ca000
reloc off = 0x887ca000
Build = 32-bit
fdt_blob = 0x905c2cb0
new_fdt = 0x905c2cb0
fdt_size = 0x00005220
```
因此我們使用 `loadb` 將 `uImage` 傳入位置 `0x90100000`
```
U-Boot 2025.04-rc4-00008-g15d6518c942f (Mar 17 2025 - 16:28:20 +0800)
DRAM: 8 MiB
Core: 73 devices, 11 uclasses, devicetree: separate
Flash: 2 MiB
Loading Environment from Flash... OK
In: serial@40011000
Out: serial@40011000
Err: serial@40011000
U-Boot > <INTERRUPT>
U-Boot > <INTERRUPT>
U-Boot > loadb 90100000
## Ready for binary (kermit) download to 0x90100000 at 115200 bps...
```
可以看到在等待檔案, 接著輸入 `Ctrl + \` + `c` 來回到命令模式並傳送 `uImage` ,待傳完之後打 `c` 回到 `U-Boot`
```
(Back at an-Pro-E500-G6-WS720T)
----------------------------------------------------
(~/Desktop/) C-Kermit>send linux/arch/arm/boot/uImage
(~/Desktop/) C-Kermit>c
Connecting to /dev/ttyUSB1, speed 115200
Escape character: Ctrl-\ (ASCII 28, FS): enabled
Type the escape character followed by C to get back,
or followed by ? to see other options.
----------------------------------------------------
## Total Size = 0x001b48d8 = 1788120 Bytes
## Start Addr = 0x90100000
```
接下來將 `dtb` 傳入位置 `0x90500000`
```
U-Boot > loadb 90500000
## Ready for binary (kermit) download to 0x90500000 at 115200 bps...
(Back at an-Pro-E500-G6-WS720T)
----------------------------------------------------
(~/Desktop/) C-Kermit>send linux/arch/arm/boot/dts/st/stm32f429-disco.dtb
(~/Desktop/) C-Kermit>c
Connecting to /dev/ttyUSB1, speed 115200
Escape character: Ctrl-\ (ASCII 28, FS): enabled
Type the escape character followed by C to get back,
or followed by ? to see other options.
----------------------------------------------------
## Total Size = 0x00004c90 = 19600 Bytes
## Start Addr = 0x90500000
```
最後在 `U-Boot` 設定核心的啟動參數
```
U-Boot > setenv bootargs 'console=ttySTM0,115200 root=/dev/ram earlyprintk'
U-Boot > bootm 90100000 - 90500000
```
就可以看到成功啟動了 `linux`
```
## Booting kernel from Legacy Image at 90100000 ...
Image Name: Linux-6.14.0-rc7-00179-gb3ee1e46
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1788056 Bytes = 1.7 MiB
Load Address: 90008000
Entry Point: 90008001
Verifying Checksum ... OK
## Flattened Device Tree blob at 90500000
Booting using the fdt blob at 0x90500000
Working FDT set to 90500000
Loading Kernel Image to 90008000
Loading Device Tree to 905b7000, end 905bec8f ... OK
Working FDT set to 905b7000
Starting kernel ...
[ 0.000000] Booting Linux on physical CPU 0x0
[ 0.000000] Linux version 6.14.0-rc7-00179-gb3ee1e460951-dirty (an@an-Pro-E500-G6-WS720T) (arm-linux-gnueabi-gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, GNU ld (GNU Binutils for Ubuntu) 2.42) #18 PREEMPT Sat Mar 22 15:48:05 CST 2025
[ 0.000000] CPU: ARMv7-M [410fc241] revision 1 (ARMv7M), cr=00000000
[ 0.000000] CPU: unknown data cache, unknown instruction cache
[ 0.000000] OF: fdt: Machine model: STMicroelectronics STM32F429i-DISCO board
[ 0.000000] printk: legacy bootconsole [earlycon0] enabled
[ 0.000000] Zone ranges:
[ 0.000000] Normal [mem 0x0000000090000000-0x00000000907fffff]
[ 0.000000] Movable zone start for each node
[ 0.000000] Early memory node ranges
[ 0.000000] node 0: [mem 0x0000000090000000-0x00000000907fffff]
[ 0.000000] Initmem setup node 0 [mem 0x0000000090000000-0x00000000907fffff]
[ 0.000000] OF: reserved mem: Reserved memory: No reserved-memory node in the DT
[ 0.000000] Kernel command line: console=ttySTM0,115200 root=/dev/ram earlyprintk
[ 0.000000] printk: log buffer data + meta data: 65536 + 204800 = 270336 bytes
[ 0.000000] Dentry cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[ 0.000000] Inode-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[ 0.000000] Built 1 zonelists, mobility grouping off. Total pages: 2048
[ 0.000000] mem auto-init: stack:all(zero), heap alloc:off, heap free:off
[ 0.000000] SLUB: HWalign=8, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[ 0.000000] rcu: Preemptible hierarchical RCU implementation.
[ 0.000000] rcu: RCU event tracing is enabled.
[ 0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 10 jiffies.
[ 0.000000] NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
[ 0.000000] /soc/interrupt-controller@40013c00: bank0
[ 0.000000] rcu: srcu_init: Setting srcu_struct sizes based on contention.
[ 0.000000] clocksource: arm_system_timer: mask: 0xffffff max_cycles: 0xffffff, max_idle_ns: 331816030 ns
[ 0.000000] ARM System timer initialized as clocksource
[ 0.000036] sched_clock: 32 bits at 90MHz, resolution 11ns, wraps every 23860929530ns
[ 0.009485] timers@40000c00: STM32 sched_clock registered
[ 0.016161] Switching to timer-based delay loop, resolution 11ns
[ 0.023345] timers@40000c00: STM32 delay timer registered
[ 0.029965] clocksource: timers@40000c00: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 21236227187 ns
[ 0.041948] /soc/timers@40000c00: STM32 clockevent driver initialized (32 bits)
[ 0.058845] Calibrating delay loop (skipped), value calculated using timer frequency.. 180.00 BogoMIPS (lpj=900000)
[ 0.071494] pid_max: default: 4096 minimum: 301
[ 0.080117] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[ 0.089280] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[ 0.157293] rcu: Hierarchical SRCU implementation.
[ 0.163581] rcu: Max phase no-delay instances is 1000.
[ 0.188171] Memory: 4588K/8192K available (1677K kernel code, 272K rwdata, 628K rodata, 240K init, 102K bss, 3240K reserved, 0K cma-reserved)
[ 0.209267] devtmpfs: initialized
[ 0.462273] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[ 0.474441] pinctrl core: initialized pinctrl subsystem
[ 0.756757] /soc/spi@40015000/display@1: Fixed dependency cycle(s) with /soc/display-controller@40016800
[ 0.769703] /soc/display-controller@40016800: Fixed dependency cycle(s) with /soc/spi@40015000/display@1
[ 0.830969] /soc/spi@40015000/display@1: Fixed dependency cycle(s) with /soc/display-controller@40016800
[ 0.853089] /soc/spi@40015000/display@1: Fixed dependency cycle(s) with /soc/display-controller@40016800
[ 0.869242] /soc/display-controller@40016800: Fixed dependency cycle(s) with /soc/spi@40015000/display@1
[ 0.956606] stm32f429-pinctrl soc:pinctrl@40020000: GPIOA bank added
[ 0.976370] stm32f429-pinctrl soc:pinctrl@40020000: GPIOB bank added
[ 1.000595] stm32f429-pinctrl soc:pinctrl@40020000: GPIOC bank added
[ 1.025950] stm32f429-pinctrl soc:pinctrl@40020000: GPIOD bank added
[ 1.048708] stm32f429-pinctrl soc:pinctrl@40020000: GPIOE bank added
[ 1.072381] stm32f429-pinctrl soc:pinctrl@40020000: GPIOF bank added
[ 1.097161] stm32f429-pinctrl soc:pinctrl@40020000: GPIOG bank added
[ 1.123180] stm32f429-pinctrl soc:pinctrl@40020000: GPIOH bank added
[ 1.149769] stm32f429-pinctrl soc:pinctrl@40020000: GPIOI bank added
[ 1.177386] stm32f429-pinctrl soc:pinctrl@40020000: GPIOJ bank added
[ 1.200420] stm32f429-pinctrl soc:pinctrl@40020000: GPIOK bank added
[ 1.209116] stm32f429-pinctrl soc:pinctrl@40020000: Pinctrl STM32 initialized
[ 1.326514] stm32-dma 40026000.dma-controller: STM32 DMA driver registered
[ 1.380118] stm32-dma 40026400.dma-controller: STM32 DMA driver registered
[ 1.400838] vcc5v-otg-regulator enforce active low on GPIO handle
[ 1.435452] clocksource: Switched to clocksource timers@40000c00
[ 1.544632] workingset: timestamp_bits=30 max_order=11 bucket_order=0
[ 1.559542] io scheduler mq-deadline registered
[ 1.565121] io scheduler kyber registered
[ 1.640383] io scheduler bfq registered
[ 2.412772] STM32 USART driver initialized
[ 2.454646] 40011000.serial: ttySTM0 at MMIO 0x40011000 (irq = 47, base_baud = 5625000) is a stm32-usart
[ 2.487278] printk: legacy console [ttySTM0] enabled
[ 2.487278] printk: legacy console [ttySTM0] enabled
[ 2.514769] printk: legacy bootconsole [earlycon0] disabled
[ 2.514769] printk: legacy bootconsole [earlycon0] disabled
[ 2.578654] stm32_rtc 40002800.rtc: registered as rtc0
[ 2.586152] stm32_rtc 40002800.rtc: setting system clock to 2000-01-01T02:29:39 UTC (946693779)
[ 2.601124] stm32_rtc 40002800.rtc: Date/Time must be initialized
[ 2.615053] i2c_dev: i2c /dev entries driver
[ 2.652100] stmpe-i2c 0-0041: stmpe811 detected, chip id: 0x811
[ 2.701109] stm32f4-i2c 40005c00.i2c: STM32F4 I2C driver registered
[ 3.156715] input: gpio-keys as /devices/platform/gpio-keys/input/input0
[ 3.188285] clk: Disabling unused clocks
[ 3.228156] Freeing unused kernel image (initmem) memory: 240K
[ 3.235916] This architecture does not have kernel memory protection.
[ 3.243884] Run /init as init process
[ 3.250807] Failed to execute /init (error -13)
[ 3.257230] Run /sbin/init as init process
/ # ls
bin dev etc init linuxrc proc sbin sys usr
```
:::warning
TODO: 改用 [winfred-lu/STM32F429I-disco_Buildroot](https://github.com/winfred-lu/STM32F429I-disco_Buildroot)
TODO: 參照 [MartinK7/stm32f429-disco-running-linux](https://github.com/MartinK7/stm32f429-disco-running-linux),以輕量級的 afboot-stm32 取代 u-boot,並使用 SD card (免除燒錄韌體)
:::
---
### TODO
1. 重建 rootfs,使用 [winfred-lu/STM32F429I-disco_Buildroot](https://github.com/winfred-lu/STM32F429I-disco_Buildroot),預期 `buildroot/output/images` 目錄可見 `rootfs.cpio`
2. 原本使用 u-boot,可改用 [afboot-stm32](https://github.com/mcoquelin-stm32/afboot-stm32),好處是精簡、可追蹤開機流程
3. 運用 [LTO](https://lwn.net/Articles/744507/) 縮減 kernel image size
4. 準備用 SD card reader
---
### 使用 afboot-stm32 取代 u-boot
下載 afboot-stm32
```bash
git clone https://github.com/mcoquelin-stm32/afboot-stm32.git
```
直接編譯後會發現這個問題
```
$ make stm32f429i-disco
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections -Os -std=gnu99 -Wall -DKERNEL_ADDR=0x08008000 -DDTB_ADDR=0x08004000 stm32f429i-disco.c -o stm32f429i-disco.o
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections -Os -std=gnu99 -Wall -DKERNEL_ADDR=0x08008000 -DDTB_ADDR=0x08004000 gpio.c -o gpio.o
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections -Os -std=gnu99 -Wall -DKERNEL_ADDR=0x08008000 -DDTB_ADDR=0x08004000 mpu.c -o mpu.o
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections -Os -std=gnu99 -Wall -DKERNEL_ADDR=0x08008000 -DDTB_ADDR=0x08004000 qspi.c -o qspi.o
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections -Os -std=gnu99 -Wall -DKERNEL_ADDR=0x08008000 -DDTB_ADDR=0x08004000 start_kernel.c -o start_kernel.o
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections -Os -std=gnu99 -Wall -DKERNEL_ADDR=0x08008000 -DDTB_ADDR=0x08004000 usart-f4.c -o usart-f4.o
arm-none-eabi-ld -T stm32f429.lds -nostartfiles --gc-sections -o stm32f429i-disco.elf stm32f429i-disco.o gpio.o mpu.o qspi.o start_kernel.o usart-f4.o
arm-none-eabi-ld: Error: unable to disambiguate: -nostartfiles (did you mean --nostartfiles ?)
make: *** [Makefile:28: stm32f429i-disco] Error 1
```
這是因為鏈接器從 `gcc` 改為直接使用 `ld`,但 `ld` 的新版本不再接受 `-nostartfiles` 選項,導致編譯錯誤( `Error: unable to disambiguate: -nostartfiles` )
修改 Makefile
```diff
- LINKERFLAGS := -nostartfiles --gc-sections
+ LINKERFLAGS := --gc-sections
```
修改完後又出現了另一個問題
```
$ make stm32f429i-disco
arm-none-eabi-ld -T stm32f429.lds --gc-sections -o stm32f429i-disco.elf stm32f429i-disco.o gpio.o mpu.o qspi.o start_kernel.o usart-f4.o
arm-none-eabi-ld: stm32f429i-disco.o: in function `reset':
stm32f429i-disco.c:(.text.reset+0x1a): undefined reference to `memcpy'
arm-none-eabi-ld: stm32f429i-disco.c:(.text.reset+0x34): undefined reference to `memset'
make: *** [Makefile:28: stm32f429i-disco] Error 1
```
因為在 `gcc` 編譯時,編譯器會將某些 `hand-written code` (例如看起來像 `memcpy` 或 `memset` 的迴圈)最佳化為對 C 標準庫函數的呼叫。但是, 在 `bare-metal` 環境中, 這些標準庫函數不可用,導致鏈接錯誤( `undefined reference to memcpy` 和 `memset` )
再對 Makefile 做以下修改
```diff
CFLAGS := -mthumb -mcpu=cortex-m4
CFLAGS += -ffunction-sections -fdata-sections
CFLAGS += -Os -std=gnu99 -Wall
+CFLAGS += -fno-builtin
LINKERFLAGS := -nostartfiles --gc-sections
```
修改完之後執行
```
$ make clean
$ make stm32f429i-disco
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections -Os -std=gnu99 -Wall -fno-builtin -DKERNEL_ADDR=0x08008000 -DDTB_ADDR=0x08004000 stm32f429i-disco.c -o stm32f429i-disco.o
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections -Os -std=gnu99 -Wall -fno-builtin -DKERNEL_ADDR=0x08008000 -DDTB_ADDR=0x08004000 gpio.c -o gpio.o
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections -Os -std=gnu99 -Wall -fno-builtin -DKERNEL_ADDR=0x08008000 -DDTB_ADDR=0x08004000 mpu.c -o mpu.o
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections -Os -std=gnu99 -Wall -fno-builtin -DKERNEL_ADDR=0x08008000 -DDTB_ADDR=0x08004000 qspi.c -o qspi.o
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections -Os -std=gnu99 -Wall -fno-builtin -DKERNEL_ADDR=0x08008000 -DDTB_ADDR=0x08004000 start_kernel.c -o start_kernel.o
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections -Os -std=gnu99 -Wall -fno-builtin -DKERNEL_ADDR=0x08008000 -DDTB_ADDR=0x08004000 usart-f4.c -o usart-f4.o
arm-none-eabi-ld -T stm32f429.lds --gc-sections -o stm32f429i-disco.elf stm32f429i-disco.o gpio.o mpu.o qspi.o start_kernel.o usart-f4.o
arm-none-eabi-objcopy -Obinary stm32f429i-disco.elf stm32f429i-disco.bin
arm-none-eabi-size stm32f429i-disco.elf
text data bss dec hex filename
1708 0 0 1708 6ac stm32f429i-disco.elf
```
就會看到目錄中多了 `stm32f429i-disco.bin` ,之後將它燒入開發板中
```
$ st-flash write stm32f429i-disco.bin 0x08000000
st-flash 1.8.0-99-gb149e24
2025-05-04T19:43:05 INFO common_legacy.c: STM32F42x_F43x: 256 KiB SRAM, 2048 KiB flash in at least 16 KiB pages.
file stm32f429i-disco.bin md5 checksum: b27515311742bd4736eec8ee740f828, stlink checksum: 0x00033fd4
2025-05-04T19:43:05 INFO common_flash.c: Attempting to write 2258 (0x8d2) bytes to stm32 address: 134217728 (0x8000000)
EraseFlash - Sector:0x0 Size:0x4000 -> Flash page at 0x8000000 erased (size: 0x4000)
2025-05-04T19:43:05 INFO flash_loader.c: Starting Flash write for F2/F4/F7/L4
2025-05-04T19:43:05 INFO flash_loader.c: Successfully loaded flash loader in sram
2025-05-04T19:43:05 INFO flash_loader.c: Clear DFSR
2025-05-04T19:43:05 INFO flash_loader.c: Clear CFSR
2025-05-04T19:43:05 INFO flash_loader.c: Clear HFSR
2025-05-04T19:43:05 INFO flash_loader.c: enabling 32-bit flash writes
2025-05-04T19:43:05 INFO common_flash.c: Starting verification of write complete
2025-05-04T19:43:05 INFO common_flash.c: Flash written and verified! jolly good!
```
### 改用 neocon 簡化操作
下載 neocon 原始碼
```bash
git clone https://github.com/sysprog21/neocon.git
```
進入 neocon 執行
```
make
```
之後依照自己的板子的 `ttyUSB*` 做執行 (* 替換為數字)
```shell
$ sudo chmod 666 /dev/ttyUSB*
./neocon /dev/ttyUSB*
```
### Buildroot
Buildroot 是一個用於建立嵌入式 Linux 系統的建構框架,它可以用來自動化建構包含 `root filesystem` 、`toolchain` 、 `bootloader` 、 `Linux kernel` 及應用程式等元件的整個系統映像。整個 Buildroot 系統是透過 `Kconfig` 和 `Makefile` 進行配置,使用方式與編譯 Linux kernel 類似,可以透過 `menuconfig` 等工具進行設定。雖然 Buildroot 通常會搭配 Linux kernel 使用,但它也可以在沒有 kernel 的情況下建立檔案系統,例如用於 `bare-metal` 環境
#### 為什麼需要Buildroot ?
當我們編譯 `Linux` 系統時,往往需要自行準備交叉編譯器(cross-compiler)、解決套件編譯相依性、手動建構 kernel、rootfs 和 boot loader,過程繁瑣易出錯。
`Buildroot` 正是為了簡化這一流程而設計的工具。它提供一個整合式的建構系統,讓你可以:
- 選擇目標平台與 kernel 版本
- 自動下載與編譯 BusyBox 、 toolchain 、 bootloader 等元件
- 建立最小可運作的 root filesystem(initramfs 或獨立掛載)
- 透過 `menuconfig` 介面進行自定義設定
- 透過幾個命令自動化建構整個可開機的 Linux 映像
適合嵌入式開發,能大幅減少設定錯誤與整合時間
下載 Buildroot 原始碼 :
```bash
git clone https://github.com/buildroot/buildroot
```
使用預設的 config
```
make stm32f429_disco_xip_defconfig
```
選擇我們需要的特定 Linux Kernel 版本
```
make menuconfig
```
位置: Kernel $\to$ Kernel version

需要做的更動
```
6.1.133 -> 6.14.5
```
以及 `In-tree Device Tree Source file names` 位置
```
stm32f429-disco -> st/stm32f429-disco
```

接著更改用來建構 C 標頭檔( kernel headers ) 的來源版本
位置 : Toolchain $\to$ Custom kernel headers series
```
6.1.x -> 6.13.x or later
```

另外我們在編譯時, Buildroot 為了安全性會去強制驗證下載的 `linux-6.14.5.tar.xz` 的 SHA256 雜湊值是否與 Buildroot 官方所設定的一致
為避免因為沒有對應的雜湊, 所以先將強制驗證關閉
位置 : Build options $\to$ Advanced $\to$ Force all downloads to have a valid hash

將設定保存後, 緊接著就是編譯
```
make -j12
```
完成後會在 `/buildroot/output/images/` 看到 `xipImage`
```
$ ls -lh xipImage
-rw-r--r-- 1 an an 2.6M May 8 16:26 xipImage
```
### 運用 [LTO](https://lwn.net/Articles/744507/) 縮減 kernel image size
#### LTO 的主要概念
1. 跨檔案的死碼移除( Dead Code Elimination )
- 一般情況下,編譯器無法判斷某些非 `static` 函式是否會在其他模組中被呼叫,因此不會刪除
- 使用 LTO,即便這些函數來自不同的檔案,只要沒有被用到就會被刪除,有效減少最終二進位檔大小
2. 函數內嵌展開( Inlining )與常數折疊( Constant Folding )
- LTO 能自動將函數內聯至使用者處,尤其是 `static inline` 函數,使程式碼更加緊湊
- 若參數為常數,也能提早做數值運算,減少執行時的指令數量
要在 Linux 核心啟用 Link Time Optimization (LTO) 建議使用 Clang/LLVM , 主要是 :
1. Clang/LLVM 對 LTO 的原生支援:
- LLVM 工具鏈內建了對 LTO 的強大支援。LTO 是一種編譯器優化技術,允許在連結階段對整個程式碼進行跨模組的分析和優化,從而產生更高效的程式碼。Clang 作為 LLVM 的前端,與 LLVM 的後端工具(如 lld 連結器)緊密整合,能夠高效地執行 LTO
- 相較之下,GNU 工具鏈(GCC 和 binutils)的 LTO 實現(透過 gcc 和 ld) 在 Linux 核心的背景下,支援度和穩定性可能不如 Clang/LLVM,尤其是在複雜的核心編譯場景中
2. Linux 核心對 Clang/LLVM LTO 的支援:
- Linux 核心的建構系統(Kbuild)已經針對 Clang 的 LTO 進行了專門的優化和測試
- 雖然 GCC 也支援 LTO,但核心社群對 GCC LTO 的支援和測試不如 Clang 完善,特別是在某些架構(如 ARM64 或 RISC-V)上,Clang 的 LTO 實現更為穩定且廣泛使用
3. Clang/LLVM 的模組化與效能優勢:
- LLVM 的模組化設計使得 LTO 的實現更靈活,能夠更好地處理 Linux 核心這樣的大型專案。Clang/LLVM 的 LTO 能執行更進階的優化,例如函數內嵌、死碼移除和全局變數優化,從而顯著縮減核心二進位檔案大小並提升執行效能
- 例如,Google 和 Android 等專案廣泛使用 Clang 編譯的核心,並啟用 LTO,以實現更小的核心映像和更快的執行速度,這在嵌入式系統和資料中心中尤為重要
```
sudo apt install clang lld llvm
```
```
make LLVM=1 ARCH=arm menuconfig
```
```
make LLVM=1 ARCH=arm LLVM_IAS=1 CROSS_COMPILE=arm-linux-gnueabi-
```
實驗進行中....
### 使用 SD card (免除燒錄韌體)
##### <font color="#f00">目前使用的 Linux 版本為 6.1.133 測試 SD card功能</font>
為了避免在實務過程中反覆的燒錄韌體, 所以將必要的 Linux Kernel image 以及 Device Tree 燒錄在 SD card 並且透過 SPI 傳輸資料
| STM32 PIN | VCP |
| --------- | ---- |
| PE2 | SPI4_SCK |
| PE4 | SPI4_NSS |
| PE5 | SPI4_MISO |
| PE6 | SPI4_MOSI |
USB to TTL 接線:
- PE2 $\to$ SCK
- PE4 $\to$ CS
- PE5 $\to$ MISO
- PE6 $\to$ MOSI
- GND $\to$ GND
- 5V $\to$ VCC

下方是 Linux Kernel image 以及 Device Tree 擺放位置
```
# FLASH
# -----------------------------------
# AFBOOT 0x08000000 - max 2MiB
#
# SDRAM
# -----------------------------------
# KERNEL (XIP) 0x90000000 - 3MiB-32KiB = 3064KiB
# DTB 0x902F8000 - 32KiB
# FREE SDRAM 0x90300000 5MiB
#
# SDCARD
# -----------------------------------
# Master Boot Record (MBR)
# 1. Partition - Linux Kernel image (RAW data)
# 2. Partition - Device Tree Blob (RAW data)
# 3. Partition - Reserved
```
之後就藉由 afboot 做 :
- 初始化 SPI 與 SD 卡
- 從 SD 卡讀取 MBR 分割區資訊
- 分別將 kernel 與 device tree 從對應分割區載入至指定 SDRAM 位址
下載 afboot patch 來修改原本的程式碼
```bash
git clone https://gist.github.com/kk908676/d9d659cb64f60318505ff70d5b19bafa
```
執行
```
cd ~/Desktop/afboot-stm32/ && git apply ~/Desktop/d9d659cb64f60318505ff70d5b19bafa/afboot.patch
```
燒錄進板子內
```
cd afboot-stm32
make flash_stm32f429i-disco
```
有了 bootloader 之後我們就只需要將 Linux Kernel image 以及 Device Tree 燒錄在 SD card ,這樣 bootloader 就可以到指定的地點透過 SPI 抓取這些資料以此來啟動 kernel
在準備 image 和 device tree 時依據上方的記憶體配置圖修改 SDRAM 大小以及位置、 xipImage 執行位置
```
cd buildroot/
make linux-menuconfig
```
位置 : System Type $\to$ Set flash/sdram size and base addr

更改 (S)DRAM Base Address 為 `0x90300000`
更改 (S)DRAM SIZE 為 `0x00500000`
位置 : Boot options $\to$ Kernel Execute-In-Place from ROM

更改 XIP Kernel Physical Location 為 `0x90000000`
改完之後就能直接編譯產生我們要的 Linux Kernel image 以及 Device Tree
```
make -j12
```
會在 `buildroot/output/images` 看到 xipImage 以及 stm32f429-disco.dtb
接著就要將這兩個檔案燒錄進 SD card ,下載 :
```bash
sudo apt install genimage
```
然後還需要一個 SD card 的映像檔用來定義 SD card 的分割區結構
```
$vi sdcard.config
image sdcard.img {
hdimage {
}
partition xipImage {
image = "output/images/xipImage"
}
partition dtb {
image = "output/images/stm32f429-disco.dtb"
}
}
```
執行
```
genimage --config sdcard.config --inputpath . --outputpath .
```
就可以看到 `sdcard.img` 的映像檔了
接下來就把 SD card 插入電腦
```
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop0 7:0 0 73.9M 1 loop /snap/core22/1748
loop1 7:1 0 4K 1 loop /snap/bare/5
loop2 7:2 0 258M 1 loop /snap/firefox/5751
loop3 7:3 0 73.9M 1 loop /snap/core22/1963
loop4 7:4 0 11.1M 1 loop /snap/firmware-updater/167
loop5 7:5 0 241.9M 1 loop /snap/firefox/6159
loop6 7:6 0 516M 1 loop /snap/gnome-42-2204/202
loop7 7:7 0 91.7M 1 loop /snap/gtk-common-themes/1535
loop8 7:8 0 10.8M 1 loop /snap/snap-store/1248
loop9 7:9 0 10.8M 1 loop /snap/snap-store/1270
loop10 7:10 0 44.4M 1 loop /snap/snapd/23545
loop11 7:11 0 50.9M 1 loop /snap/snapd/24505
loop12 7:12 0 568K 1 loop /snap/snapd-desktop-integration/253
loop13 7:13 0 210.4M 1 loop /snap/thunderbird/644
loop14 7:14 0 210.2M 1 loop /snap/thunderbird/721
loop15 7:15 0 9.8M 1 loop
├─loop15p1 259:5 0 1.8M 1 part
├─loop15p2 259:6 0 19.5K 1 part
└─loop15p3 259:7 0 8M 1 part
sda 8:0 0 931.5G 0 disk
├─sda1 8:1 0 16M 0 part
└─sda2 8:2 0 931.5G 0 part
sdb 8:16 0 1.8T 0 disk
├─sdb1 8:17 0 409.9G 0 part /
├─sdb2 8:18 0 1G 0 part /boot/efi
├─sdb3 8:19 0 30G 0 part [SWAP]
└─sdb4 8:20 0 956.5G 0 part /home
sdc 8:32 1 2M 0 disk /media/an/DIS_F429ZI
sdd 8:48 1 15G 0 disk
└─sdd1 8:49 1 15G 0 part
nvme0n1 259:0 0 465.8G 0 disk
├─nvme0n1p1 259:1 0 529M 0 part
├─nvme0n1p2 259:2 0 99M 0 part
├─nvme0n1p3 259:3 0 16M 0 part
└─nvme0n1p4 259:4 0 465.1G 0 part
```
可以看到 `sdd` 就是剛剛插入大小為 15G 的 SD card
```
sdd 8:48 1 15G 0 disk
└─sdd1 8:49 1 15G 0 part
```
透過以下命令將 `sdcard.img` 燒錄進 SD card
```
sudo dd if=sdcard.img of=/dev/sdd bs=4M status=progress conv=fsync
```
便能看到 `Linux Kernel image` 和 `Device Tree`
```
sdd 8:48 1 15G 0 disk
├─sdd1 8:49 1 1.6M 0 part
└─sdd2 8:50 1 18.5K 0 part
```
隨後將 SD card 插入 microsd card adapter
待 bootloader 成功讀抓取到這些資料後便能啟動 Linux kernel
```
Booting ...
Reading MBR ...
Loading kernel ...
..........................
Loading device tree blob ...
...................
Executing kernel ...
** 7 printk messages dropped **
[ 0.000000] Normal [mem 0x0000000090300000-0x00000000907fffff]
[ 0.000000] Movable zone start for each node
[ 0.000000] Early memory node ranges
[ 0.000000] node 0: [mem 0x0000000090300000-0x00000000907fffff]
[ 0.000000] Initmem setup node 0 [mem 0x0000000090300000-0x00000000907fffff]
[ 0.000000] On node 0, zone Normal: 768 pages in unavailable ranges
[ 0.000000] pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
[ 0.000000] pcpu-alloc: [0] 0
[ 0.000000] Built 1 zonelists, mobility grouping off. Total pages: 1270
[ 0.000000] Kernel command line: root=/dev/ram
[ 0.000000] Dentry cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[ 0.000000] Inode-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[ 0.000000] mem auto-init: stack:all(zero), heap alloc:off, heap free:off
[ 0.000000] Memory: 4804K/5120K available (951K kernel code, 69K rwdata, 216K rodata, 72K init, 45K bss, 316K reserved, 0K cma-reserved)
[ 0.000000] rcu: Preemptible hierarchical RCU implementation.
[ 0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 100 jiffies.
[ 0.000000] NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
[ 0.000000] /soc/interrupt-controller@40013c00: bank0
[ 0.000000] rcu: srcu_init: Setting srcu_struct sizes based on contention.
[ 0.000000] clocksource: arm_system_timer: mask: 0xffffff max_cycles: 0xffffff, max_idle_ns: 355517175 ns
[ 0.000000] ARM System timer initialized as clocksource
[ 0.000026] sched_clock: 32 bits at 84MHz, resolution 11ns, wraps every 25565281274ns
[ 0.000490] timers@40000c00: STM32 sched_clock registered
[ 0.000894] Switching to timer-based delay loop, resolution 11ns
[ 0.001128] timers@40000c00: STM32 delay timer registered
[ 0.001478] clocksource: timers@40000c00: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 22753100554 ns
[ 0.002093] /soc/timers@40000c00: STM32 clockevent driver initialized (32 bits)
[ 0.009570] Calibrating delay loop (skipped), value calculated using timer frequency.. 168.00 BogoMIPS (lpj=84000)
[ 0.010091] pid_max: default: 4096 minimum: 301
[ 0.011757] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[ 0.012352] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[ 0.046812] rcu: Hierarchical SRCU implementation.
[ 0.046973] rcu: Max phase no-delay instances is 400.
[ 0.059041] devtmpfs: initialized
[ 0.748431] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1911260446275000 ns
[ 0.749793] pinctrl core: initialized pinctrl subsystem
[ 1.108871] /soc/spi@40015000/display@1: Fixed dependency cycle(s) with /soc/display-controller@40016800
[ 1.110786] /soc/display-controller@40016800: Fixed dependency cycle(s) with /soc/spi@40015000/display@1
[ 1.173585] /soc/spi@40015000/display@1: Fixed dependency cycle(s) with /soc/display-controller@40016800
[ 1.193676] /soc/spi@40015000/display@1: Fixed dependency cycle(s) with /soc/display-controller@40016800
[ 1.207708] /soc/display-controller@40016800: Fixed dependency cycle(s) with /soc/spi@40015000/display@1
[ 1.370683] stm32f429-pinctrl soc:pinctrl@40020000: GPIOA bank added
[ 1.393696] stm32f429-pinctrl soc:pinctrl@40020000: GPIOB bank added
[ 1.418110] stm32f429-pinctrl soc:pinctrl@40020000: GPIOC bank added
[ 1.454932] stm32f429-pinctrl soc:pinctrl@40020000: GPIOD bank added
[ 1.479779] stm32f429-pinctrl soc:pinctrl@40020000: GPIOE bank added
[ 1.506033] stm32f429-pinctrl soc:pinctrl@40020000: GPIOF bank added
[ 1.533503] stm32f429-pinctrl soc:pinctrl@40020000: GPIOG bank added
[ 1.569453] stm32f429-pinctrl soc:pinctrl@40020000: GPIOH bank added
[ 1.606354] stm32f429-pinctrl soc:pinctrl@40020000: GPIOI bank added
[ 1.643034] stm32f429-pinctrl soc:pinctrl@40020000: GPIOJ bank added
[ 1.670745] stm32f429-pinctrl soc:pinctrl@40020000: GPIOK bank added
[ 1.671841] stm32f429-pinctrl soc:pinctrl@40020000: Pinctrl STM32 initialized
[ 2.188840] stm32-dma 40026000.dma-controller: STM32 DMA driver registered
[ 2.316773] stm32-dma 40026400.dma-controller: STM32 DMA driver registered
[ 2.358368] clocksource: Switched to clocksource timers@40000c00
[ 2.493684] workingset: timestamp_bits=30 max_order=11 bucket_order=0
[ 3.620731] STM32 USART driver initialized
[ 3.669996] 40011000.serial: ttySTM0 at MMIO 0x40011000 (irq = 47, base_baud = 5250000) is a stm32-usart
[ 4.486832] printk: console [ttySTM0] enabled
[ 4.538656] random: crng init done
[ 4.590596] stm32_rtc 40002800.rtc: registered as rtc0
[ 4.597101] stm32_rtc 40002800.rtc: setting system clock to 2000-01-01T00:00:35 UTC (946684835)
[ 4.611273] stm32_rtc 40002800.rtc: Date/Time must be initialized
[ 5.589741] clk: Disabling unused clocks
[ 5.613033] Freeing unused kernel image (initmem) memory: 24K
[ 5.620087] This architecture does not have kernel memory protection.
[ 5.627857] Run /init as init process
[ 5.632266] with arguments:
[ 5.636088] /init
[ 5.639232] with environment:
[ 5.643288] HOME=/
[ 5.646647] TERM=linux
[ 7.643876] S01seedrng: page allocation failure: order:7, mode:0xcc0(GFP_KERNEL), nodemask=(null)
[ 7.655967] CPU: 0 PID: 33 Comm: S01seedrng Not tainted 6.1.133 #4
[ 7.663247] Hardware name: STM32 (Device Tree Support)
[ 7.669164] Function entered at [<90002b52>] from [<90001f9b>]
[ 7.675925] Function entered at [<90001f9b>] from [<900d9b45>]
[ 7.682691] Function entered at [<900d9b45>] from [<90046edb>]
[ 7.689470] Function entered at [<90046edb>] from [<90047259>]
[ 7.696234] Function entered at [<90047259>] from [<90047677>]
[ 7.703006] Function entered at [<90047677>] from [<90047889>]
[ 7.709659] Function entered at [<90047889>] from [<90044765>]
[ 7.716546] Function entered at [<90044765>] from [<9003f9dd>]
[ 7.723321] Function entered at [<9003f9dd>] from [<9006a9e9>]
[ 7.730086] Function entered at [<9006a9e9>] from [<9004f3f3>]
[ 7.736974] Function entered at [<9004f3f3>] from [<9004f6cb>]
[ 7.743634] Function entered at [<9004f6cb>] from [<900500ad>]
[ 7.750511] Function entered at [<900500ad>] from [<90000061>]
[ 7.757233] Exception stack(0x9041ffa8 to 0x9041fff0)
[ 7.763459] ffa0: 9031f698 9031f658 9031f698 9031f658 9055f648 0000000b
[ 7.773023] ffc0: 9031f698 9031f658 905dca70 0000000b 9031f698 9031f368 0000000d 00000000
[ 7.782384] ffe0: 00000000 905dca70 9058f6b3 90596964
[ 7.788897] Mem-Info:
[ 7.791830] active_anon:0 inactive_anon:0 isolated_anon:0
[ 7.791830] active_file:0 inactive_file:0 isolated_file:0
[ 7.791830] unevictable:270 dirty:0 writeback:0
[ 7.791830] slab_reclaimable:0 slab_unreclaimable:397
[ 7.791830] mapped:0 shmem:0 pagetables:0
[ 7.791830] sec_pagetables:0 bounce:0
[ 7.791830] kernel_misc_reclaimable:0
[ 7.791830] free:283 free_pcp:0 free_cma:0
[ 7.833706] Node 0 active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:1080kB isolated(anon):0kB isolated(file):0kB mapped:0kB dirty:0kB writeback:0kB shmem:0kB writeback_tmp:0kB kernel_stack:176kB pagetables:0kB sec_pagetables:0kB all_unreclaimable? no
[ 7.862644] Normal free:1132kB boost:0kB min:276kB low:344kB high:412kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:1080kB writepending:0kB present:5120kB managed:4828kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
[ 7.892809] lowmem_reserve[]: 0 0
[ 7.897660] Normal: 1*4kB (U) 1*8kB (U) 0*16kB 1*32kB (U) 1*64kB (U) 4*128kB (U) 2*256kB (U) 0*512kB 0*1024kB 0*2048kB 0*4096kB = 1132kB
[ 7.917635] 276 total pagecache pages
[ 7.922090] 1280 pages RAM
[ 7.925692] 0 pages HighMem/MovableOnly
[ 7.930547] 73 pages reserved
[ 7.934253] nommu: Allocation of length 380928 from process 33 (S01seedrng) failed
[ 7.943652] active_anon:0 inactive_anon:0 isolated_anon:0
[ 7.943652] active_file:0 inactive_file:0 isolated_file:0
[ 7.943652] unevictable:270 dirty:0 writeback:0
[ 7.943652] slab_reclaimable:0 slab_unreclaimable:397
[ 7.943652] mapped:0 shmem:0 pagetables:0
[ 7.943652] sec_pagetables:0 bounce:0
[ 7.943652] kernel_misc_reclaimable:0
[ 7.943652] free:283 free_pcp:0 free_cma:0
[ 7.984977] Node 0 active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:1080kB isolated(anon):0kB isolated(file):0kB mapped:0kB dirty:0kB writeback:0kB shmem:0kB writeback_tmp:0kB kernel_stack:176kB pagetables:0kB sec_pagetables:0kB all_unreclaimable? no
[ 8.013762] Normal free:1132kB boost:0kB min:276kB low:344kB high:412kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:1080kB writepending:0kB present:5120kB managed:4828kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
[ 8.043815] lowmem_reserve[]: 0 0
[ 8.048685] Normal: 1*4kB (U) 1*8kB (U) 0*16kB 1*32kB (U) 1*64kB (U) 4*128kB (U) 2*256kB (U) 0*512kB 0*1024kB 0*2048kB 0*4096kB = 1132kB
[ 8.068712] 276 total pagecache pages
[ 8.073247] binfmt_flat: Unable to allocate RAM for process text/data, errno -12
Welcome to Buildroot
(none) login:
```