Chẩn đoán lỗi Kernel Panic hoặc Hang khi Boot
Xác định điểm chết của Kernel qua Log
Khi hệ thống bị treo (hang) hoặc sập (panic) ngay sau khi khởi động, màn hình console thường chỉ hiện dòng cuối cùng hoặc đen thui. Nhiệm vụ đầu tiên là đọc lại buffer log cuối cùng để biết lỗi xảy ra ở đâu.
Thực hiện lệnh sau trên host (máy chủ build) hoặc trên máy ảo nếu đã chèn UART, hoặc trực tiếp trên board nếu còn kịp gõ lệnh:
dmesg | grep -i "panic\|error\|fail\|abort" | tail -n 20
Kết quả mong đợi: Bạn sẽ thấy các dòng log báo lỗi cụ thể như "Kernel panic - not syncing: VFS: Unable to mount root fs" hoặc "Call Trace" chỉ rõ hàm gây lỗi.
Kiểm tra tham số khởi động Kernel (Boot Arguments)
Nhiều lỗi boot do sai tham số truyền vào Kernel từ U-Boot, đặc biệt là địa chỉ ram hoặc tên device rootfs. Cần kiểm tra lại chuỗi `bootargs` đang được U-Boot thiết lập.
Trong môi trường U-Boot (khi board còn khởi động được đến bootloader), gõ lệnh:
printenv bootargs
Kết quả mong đợi: Hiển thị chuỗi cấu hình đầy đủ. Ví dụ: `console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait rw`.
Nếu bạn đang debug từ máy chủ host thông qua U-Boot source code, hãy kiểm tra file cấu hình board:
cat arch/arm/dts/hackberrypi-cm5.dts | grep -A 5 "chosen"
Kết quả mong đợi: Xem phần `chosen` trong file DTS, nơi định nghĩa `bootargs`. Nếu không khớp với yêu cầu phần cứng, đây là nguyên nhân.
Chỉnh sửa Kernel Config để bật Panic Logging
Mặc định Kernel có thể tắt một số log chi tiết khi panic để tiết kiệm tài nguyên. Cần bật tính năng này để ghi log ra file hoặc console đầy đủ hơn.
Mở file cấu hình Kernel của bạn (thường là `.config` trong thư mục root source):
cd /path/to/linux-source
menuconfig
Trong menu, đi theo đường dẫn: `Kernel hacking` -> `Printk and Console Log` -> `Enable early console` và `Log kernel messages to a file` (nếu cần). Hoặc bật `CONFIG_PANIC_TIMEOUT` để tự động reboot sau panic.
Để sửa trực tiếp file config nhanh chóng, thêm dòng sau vào file `.config`:
echo "CONFIG_PANIC_TIMEOUT=10" >> .config
echo "CONFIG_CONSOLE_LOGLEVEL_DEFAULT=8" >> .config
Sau đó chạy lệnh rebuild config:
make olddefconfig
Kết quả mong đợi: Không có lỗi conflict, file `.config` đã được cập nhật sẵn sàng cho bước compile tiếp theo.
Sử dụng UART để Debug sâu các vấn đề khởi động
Cấu hình kết nối UART từ Host
UART là kênh giao tiếp trực tiếp nhất giữa CPU và bên ngoài, không phụ thuộc vào driver đồ họa hay mạng. Đây là công cụ số 1 để debug khi màn hình đen.
Trên máy chủ Linux, xác định thiết bị serial đang gắn (thường là `/dev/ttyUSB0` cho USB-to-TTL hoặc `/dev/ttyS0` cho UART on-board). Cấu hình kết nối với tốc độ 115200 baud:
screen /dev/ttyUSB0 115200
Hoặc dùng `minicom` nếu không có `screen`:
stty -F /dev/ttyUSB0 115200 cs8 -cstopb -parenb
cat /dev/ttyUSB0
Kết quả mong đợi: Terminal hiện lên màn hình đen, khi reset board sẽ thấy dòng log đầu tiên của U-Boot hoặc Kernel hiện ra ngay lập tức.
Định hướng log Kernel ra UART trong DTS
Đảm bảo file Device Tree Source (DTS) của HackberryPi CM5 đã chỉ định đúng cổng UART làm console mặc định. Nếu sai, Kernel sẽ không in log ra UART mà có thể ra VGA (không có tín hiệu).
Chỉnh sửa file DTS: `arch/arm/dts/hackberrypi-cm5.dts` (hoặc file .dtsi liên quan). Tìm node `chosen` và chỉnh sửa thuộc tính `stdout-path`.
chosen {
bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p2 rw";
stdout-path = "serial0:115200n8";
};
Cũng cần đảm bảo node UART được bật trong file DTS:
&uart0 {
status = "okay";
};
Kết quả mong đợi: Khi compile và flash lại, dòng log "Kernel command line: ..." sẽ hiện rõ trên màn hình `screen` của bạn.
Debug hang ở giai đoạn sớm (Early Printk)
Nếu hệ thống treo ngay khi Kernel mới load, log bình thường chưa kịp chạy. Cần kích hoạt `early_printk` để bắt các lỗi ở mức độ thấp nhất.
Sửa file `.config` của Kernel:
grep "CONFIG_EARLY_PRINTK" .config
# Nếu chưa có hoặc bị #, hãy thêm:
echo "CONFIG_EARLY_PRINTK=y" >> .config
echo "CONFIG_EARLY_PRINTK_CONSOLE=y" >> .config
make olddefconfig
Trong file DTS, đảm bảo tham số console trong bootargs có đúng cổng:
bootargs = "console=earlycon0,115200 console=ttyS0,115200 ...";
Kết quả mong đợi: Khi boot, bạn sẽ thấy log hiện ra ngay cả khi driver UART chính thức chưa được load, giúp xác định lỗi ở giai đoạn thiết lập memory hoặc CPU.
Xử lý lỗi driver cho Camera, WiFi hoặc Bluetooth
Debug Driver Camera (I2C/MII)
Lỗi camera thường do xung đột I2C hoặc sai cấu hình pin control. Kiểm tra xem driver đã load và thiết bị đã được nhận diện chưa.
Thực hiện lệnh trên board đang chạy:
dmesg | grep -i "camera\|i2c\|v4l2"
Kiểm tra xem node I2C có hiện trong hệ thống không:
ls /sys/class/i2c-adapter/
Kết quả mong đợi: Nếu thấy lỗi "I2C communication error" hoặc "No such device", cần kiểm tra lại đường dẫn trong DTS (kích thước bus, địa chỉ slave).
Chỉnh sửa file DTS để sửa địa chỉ I2C hoặc clock frequency:
/* Ví dụ trong hackberrypi-cm5.dts */
&i2c1 {
camera@36 {
compatible = "ov,ov5640";
reg = ;
status = "okay";
};
};
Kết quả mong đợi: Sau khi rebuild DTS và boot lại, `dmesg` sẽ hiện "Camera initialized successfully".
Debug Driver WiFi/Bluetooth (SDIO/UART)
WiFi và Bluetooth trên CM5 thường dùng chip combo (như BCM43xx) chạy qua SDIO hoặc UART. Lỗi thường gặp là thiếu firmware hoặc sai clock.
Kiểm tra xem firmware đã được mount vào rootfs chưa:
ls /lib/firmware/ | grep -i brcm\|rtl\|ath
Thiếu firmware là nguyên nhân phổ biến nhất gây lỗi "Failed to load firmware". Nếu thiếu, hãy copy file firmware từ package `linux-firmware` vào thư mục tương ứng trong rootfs.
Kiểm tra trạng thái driver trong kernel:
modinfo brcmfmac
dmesg | grep -i "brcmf\|sdio\|bt"`
Kết quả mong đợi: Nếu thấy "Firmware not found", hãy copy file `.bin` tương ứng vào `/lib/firmware` trên rootfs và rebuild image.
Force Load Driver khi thiết bị không tự nhận
Đôi khi driver có trong kernel nhưng không tự động load do thiếu thiết bị trong DTS hoặc module bị blacklist.
Thủ công load module:
modprobe brcmfmac
modprobe btusb
Nếu vẫn lỗi, kiểm tra xem module có bị blacklist không trong file `/etc/modprobe.d/blacklist.conf` (nếu có) hoặc trong config kernel.
Để sửa permanent, thêm vào file DTS phần `status = "okay"` cho node WiFi/Bluetooth:
&sdhci2 {
status = "okay";
/* Cấu hình bus-width, max-frequency nếu cần */
};
Kết quả mong đợi: Lệnh `ifconfig` hoặc `ip link` sẽ hiện interface `wlan0` mới được tạo.
Mẹo tùy chỉnh Kernel Source và Rebuild nhanh (Incremental Build)
Cấu hình Makefile cho Build song song
Việc rebuild toàn bộ Kernel mất nhiều thời gian. Để tối ưu, luôn sử dụng nhiều CPU cores và tận dụng tính năng incremental của `make`.
Thực hiện lệnh build với số cores tự động (giả sử 4 cores):
make -j4
Để build chỉ module mới thay đổi (ví dụ module camera):
make -j4 modules
Kết quả mong đợi: Thời gian build giảm đáng kể, chỉ compile các file có timestamp mới hơn file object (.o).
Sử dụng ccache để tăng tốc biên dịch
Cache kết quả biên dịch của các file C không đổi là cách nhanh nhất để giảm thời gian build từ 30 phút xuống 5 phút cho các lần sửa nhỏ.
Đảm bảo đã cài đặt `ccache` trên host:
apt-get install ccache
Thiết lập biến môi trường trước khi chạy make:
export CC="ccache gcc"
export LD="ccache ld"
make -j4
Kết quả mong đợi: Lần build đầu tiên sẽ hơi lâu để build cache, các lần sau khi chỉ sửa 1 file sẽ gần như tức thì.
Rebuild chỉ Kernel Image (vmlinuz) sau khi sửa config
Nếu bạn chỉ thay đổi cấu hình (`.config`) hoặc file DTS mà không động vào source code C, không cần build toàn bộ modules.
Chỉ cần build lại image bootable:
make -j4 zImage
Hoặc nếu dùng format khác như Image.gz:
make -j4 Image.gz
Sau đó copy file này vào thư mục boot của rootfs:
cp arch/arm/boot/zImage /path/to/rootfs/boot/
Kết quả mong đợi: Image mới được tạo chỉ trong vài giây, không cần rebuild cả hệ thống.
Verify kết quả cuối cùng
Để đảm bảo mọi thay đổi đã được áp dụng chính xác, hãy kiểm tra checksum của file boot và kernel version.
sha256sum arch/arm/boot/zImage
uname -r
Kết quả mong đợi: Kernel version hiện trên board (`uname -r`) phải khớp với version bạn vừa build, và checksum file image phải thay đổi so với bản cũ.
Điều hướng series:
Mục lục: Series: Xây dựng hệ điều hành Linux từ source cho HackberryPi CM5
« Phần 8: Cấu hình hệ thống sau khi boot và tối ưu hiệu năng