GRUB¶
GNU GRUB (GRand Unified Bootloader) là bootloader mặc định của hầu hết bản phân phối Linux trên PC và server. Bản đang dùng là GRUB 2 (Fedora/RHEL gọi là grub2); GRUB Legacy (0.97) đã ngừng phát triển. Vị trí của bootloader trong chuỗi boot xem tại Bootloader trong hệ nhúng Linux.
GRUB trong hệ nhúng¶
Khác với U-Boot, GRUB không khởi tạo phần cứng: nó chạy sau firmware (BIOS hoặc UEFI), firmware đã cấu hình xong CPU, DRAM và cung cấp dịch vụ đọc disk, console. Vì vậy GRUB gần như không xuất hiện trên SoC ARM nhỏ, nhưng hay gặp trong các trường hợp:
- Máy x86 nhúng / công nghiệp: IPC, gateway, thiết bị mạng dùng CPU Intel Atom, AMD Ryzen Embedded. Firmware UEFI có sẵn trên board.
- ARM64 có UEFI: server ARM64 (EDK2), hoặc board nhúng mà U-Boot đóng vai trò firmware UEFI (lệnh
bootefi). U-Boot nạpgrubaa64.efitừ phân vùng EFI, GRUB nạp kernel. Đây là cách các bản phân phối "generic" (Debian, Fedora, Ubuntu) boot được trên nhiều board ARM64 bằng một image. - Thiết bị cần Secure Boot theo chuẩn UEFI, dùng chuỗi shim → GRUB → kernel được ký.
| U-Boot | GRUB | |
|---|---|---|
| Khởi tạo phần cứng (clock, DDR) | Có (SPL) | Không, dựa vào firmware |
| Nền tảng | ARM, RISC-V, MIPS, PowerPC... | x86 BIOS/UEFI, ARM64/RISC-V UEFI |
| Cấu hình | Environment + script | grub.cfg (ngôn ngữ giống shell) |
| Định dạng kernel | zImage, Image, uImage, FIT |
bzImage, Image (EFI stub) |
| Device tree | Tự nạp và fixup | Thường do firmware cung cấp; có lệnh devicetree |
| Secure Boot | FIT signature (verified boot) | UEFI Secure Boot qua shim, hoặc chữ ký GPG |
Quá trình boot¶
BIOS (legacy)¶
- BIOS đọc sector đầu tiên (MBR) của disk. Phần code trong MBR là
boot.imgcủa GRUB, chỉ đủ để nạp sector đầu củacore.img. core.imgchứa kernel của GRUB cùng các module tối thiểu để đọc được filesystem chứa/boot/grub. Với disk MBR,core.imgnằm ở khoảng trống sau MBR (trước phân vùng đầu, thường 1 MB). Với disk GPT, cần một BIOS boot partition (typeEF02, khoảng 1 MB, không format).core.imgnạp thêm module từ/boot/grub/i386-pc/, đọcgrub.cfgvà hiện menu.
UEFI¶
- Firmware UEFI đọc biến
Boot####trong NVRAM (xem bằngefibootmgr -v) để biết file EFI cần chạy trên EFI System Partition (ESP, FAT32, typeEF00). Nếu không có biến nào, firmware chạy file mặc định\EFI\BOOT\BOOTX64.EFI(ARM64:BOOTAA64.EFI). - Khi bật Secure Boot, file đầu tiên thường là
shimx64.efi(ký bằng key của Microsoft), shim verify và chạygrubx64.efi(ký bằng key của bản phân phối). - GRUB là một ứng dụng EFI: nó đọc
grub.cfg, nạp kernel và initrd, rồi chuyển giao cho kernel.
Bố trí thường gặp trên Debian/Ubuntu:
/boot/efi/ # ESP (FAT32), mount tại /boot/efi
└── EFI/
├── BOOT/BOOTX64.EFI # fallback
└── debian/
├── shimx64.efi
├── grubx64.efi
└── grub.cfg # file nhỏ, chỉ trỏ tới /boot/grub/grub.cfg
/boot/
├── vmlinuz-6.12.0-amd64
├── initrd.img-6.12.0-amd64
└── grub/
├── grub.cfg # file cấu hình chính (sinh tự động)
├── grubenv # biến môi trường, 1024 byte
└── x86_64-efi/ # module
Cấu hình¶
grub.cfg sinh tự động¶
Trên bản phân phối, không sửa trực tiếp /boot/grub/grub.cfg vì nó bị ghi đè khi cập nhật kernel. File này được sinh bởi grub-mkconfig từ:
/etc/default/grub: các biến chung./etc/grub.d/: các script sinh từng phần (10_linuxtìm kernel trong/boot,30_os-probertìm hệ điều hành khác,40_customcho entry tự viết).
GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_CMDLINE_LINUX_DEFAULT="quiet"
GRUB_CMDLINE_LINUX="console=ttyS0,115200 isolcpus=2-3"
GRUB_TERMINAL="console serial"
GRUB_SERIAL_COMMAND="serial --unit=0 --speed=115200"
sudo grub-mkconfig -o /boot/grub/grub.cfg # Debian/Ubuntu: sudo update-grub
# Fedora/RHEL: grub2-mkconfig -o /boot/grub2/grub.cfg
Fedora/RHEL dùng BLS
Fedora và RHEL 8+ dùng Boot Loader Specification: mỗi kernel là một file trong /boot/loader/entries/*.conf, grub.cfg gọi lệnh blscfg để đọc các file này. Sửa tham số kernel bằng grubby --update-kernel=ALL --args="...".
GRUB_CMDLINE_LINUX là chỗ thêm tham số kernel cho tuning real-time (isolcpus, nohz_full, rcu_nocbs), xem Real-time KVM tuning.
Cú pháp grub.cfg¶
grub.cfg dùng ngôn ngữ script giống shell (biến, if, for, function). Với hệ nhúng, thường viết tay một file ngắn thay vì dùng grub-mkconfig:
set default=0
set timeout=3
serial --unit=0 --speed=115200
terminal_input serial console
terminal_output serial console
insmod part_gpt
insmod ext2
menuentry "Linux" {
search --no-floppy --fs-uuid --set=root 3f1c2a7e-0b5d-4c1e-9a6f-2d8b4e7c1a90
linux /boot/vmlinuz root=PARTUUID=9c2e...-02 rw rootwait console=ttyS0,115200
initrd /boot/initrd.img
}
menuentry "Linux (recovery)" {
search --no-floppy --label --set=root rootfs_b
linux /boot/vmlinuz root=LABEL=rootfs_b ro single
}
| Lệnh | Ý nghĩa |
|---|---|
insmod <mod> |
Nạp module (filesystem, bảng phân vùng, gzio, serial...) |
search --fs-uuid/--label/--file --set=root |
Tìm phân vùng theo UUID, label hoặc file có sẵn và gán vào biến root |
set root=(hd0,gpt2) |
Gán phân vùng trực tiếp. Đánh số disk từ 0, phân vùng từ 1 |
linux <path> <args> |
Nạp kernel và command line |
initrd <path> |
Nạp initramfs |
devicetree <path> |
Nạp DTB (ARM), khi firmware không cung cấp DTB phù hợp |
chainloader <path> |
Chạy một bootloader/ứng dụng EFI khác (ví dụ Windows Boot Manager) |
configfile <path> |
Chuyển sang đọc một grub.cfg khác |
load_env, save_env |
Đọc và ghi biến vào grubenv |
GRUB shell¶
Bấm c ở menu để vào shell, e để sửa tạm một entry (sửa không lưu, chỉ áp dụng cho lần boot đó). Shell hữu ích khi grub.cfg hỏng:
grub> ls
(hd0) (hd0,gpt1) (hd0,gpt2)
grub> ls (hd0,gpt2)/boot
grub> set root=(hd0,gpt2)
grub> linux /boot/vmlinuz root=/dev/sda2 rw
grub> initrd /boot/initrd.img
grub> boot
Khi GRUB không tìm thấy thư mục module hoặc grub.cfg (thường do UUID đổi sau khi format lại phân vùng), nó rơi vào grub rescue> với rất ít lệnh. Cách gỡ: dùng ls tìm phân vùng chứa /boot/grub, set prefix=(hd0,gpt2)/boot/grub, set root=(hd0,gpt2), insmod normal, normal. Sau khi boot được, chạy lại grub-install và grub-mkconfig.
Serial console qua IPMI SOL¶
Trên server có BMC, menu GRUB có thể xem và điều khiển từ xa qua Serial-over-LAN. GRUB không làm việc trực tiếp với IPMI, chỉ cần xuất ra đúng cổng UART mà BMC đang chuyển qua mạng, thường là COM2 (ttyS1):
GRUB_TERMINAL="console serial"
GRUB_SERIAL_COMMAND="serial --unit=1 --speed=115200" # unit=1 là COM2 / ttyS1
GRUB_CMDLINE_LINUX="console=tty0 console=ttyS1,115200n8"
Sai --unit thì phiên SOL chỉ thấy log BIOS mà không thấy menu GRUB, lệch baudrate thì ký tự bị rác. Cấu hình đầy đủ cho BIOS, kernel và các lỗi hay gặp xem tại IPMI → Serial-over-LAN.
Cài đặt và build image¶
grub-install¶
# BIOS: ghi boot.img vào MBR, core.img vào khoảng trống sau MBR
sudo grub-install --target=i386-pc /dev/sda
# UEFI: copy grubx64.efi vào ESP và tạo biến NVRAM
sudo grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=debian
Khi tạo image cho thiết bị (không chạy trên chính máy đích), thêm --removable để cài vào đường dẫn fallback \EFI\BOOT\BOOTX64.EFI và không ghi NVRAM của máy build.
Image standalone¶
Với hệ nhúng, thường đóng gói GRUB, module và grub.cfg thành một file EFI duy nhất, không cần thư mục /boot/grub trên disk:
grub-mkstandalone -O x86_64-efi -o BOOTX64.EFI \
--modules="part_gpt ext2 search linux serial" \
"boot/grub/grub.cfg=./grub.cfg"
grub-mkimage cho phép kiểm soát chi tiết hơn: chọn chính xác module nhúng vào và đặt prefix. Image nhỏ, ít module cũng thu hẹp bề mặt tấn công khi dùng Secure Boot.
Yocto¶
Yocto hỗ trợ GRUB qua recipe grub-efi. Trong local.conf hoặc file machine:
Image wic (ví dụ mkefidisk.wks) sẽ tạo ESP chứa bootx64.efi và grub.cfg. EFI_PROVIDER = "systemd-boot" là lựa chọn thay thế đơn giản hơn. Xem thêm Yocto Overview.
grubenv và A/B update¶
grubenv là file cố định 1024 byte lưu biến của GRUB, dùng để nhớ trạng thái giữa các lần boot:
grub-editenv /boot/grub/grubenv list
grub-editenv /boot/grub/grubenv set slot=b
sudo grub-reboot 1 # chỉ boot entry 1 ở lần khởi động tiếp theo
sudo grub-set-default 0 # đổi entry mặc định (cần GRUB_DEFAULT=saved)
Đây là cơ chế các framework OTA (Mender, RAUC, SWUpdate) dùng khi target là GRUB, tương tự vai trò của fw_setenv với U-Boot. Một mẫu A/B tối giản có bootcount:
load_env
if [ "${slot}" = "" ]; then set slot=a; fi
if [ "${upgrade_available}" = "1" ]; then
if [ "${bootcount}" = "3" ]; then
# Bản mới boot lỗi 3 lần: quay về slot cũ
if [ "${slot}" = "a" ]; then set slot=b; else set slot=a; fi
set upgrade_available=0
set bootcount=0
elif [ "${bootcount}" = "2" ]; then set bootcount=3
elif [ "${bootcount}" = "1" ]; then set bootcount=2
else set bootcount=1
fi
save_env slot upgrade_available bootcount
fi
menuentry "Linux slot ${slot}" {
search --no-floppy --label --set=root rootfs_${slot}
linux /boot/vmlinuz root=LABEL=rootfs_${slot} rw rootwait
}
Sau khi boot thành công, ứng dụng trong Linux đặt lại upgrade_available=0 và bootcount=0. Ngôn ngữ script của GRUB không có phép cộng, nên bộ đếm phải viết bằng các nhánh if như trên.
GRUB ghi grubenv tại chỗ
save_env ghi đè trực tiếp các block của file mà không qua driver filesystem, nên chỉ hoạt động khi grubenv nằm trên filesystem thường, không nén, không nằm trên LVM, RAID, Btrfs hay ZFS. Thường đặt grubenv trên ESP hoặc một phân vùng boot riêng.
Secure Boot¶
Có hai cách để GRUB tham gia chain of trust (khái niệm xem tại Secure boot và TrustZone):
- UEFI Secure Boot: firmware chỉ chạy file EFI có chữ ký hợp lệ theo key trong biến
db. Các bản phân phối dùng shim (ký bởi Microsoft) chứa key của bản phân phối, shim verify GRUB, GRUB verify kernel qua shim. Người dùng thêm key riêng (để chạy module/kernel tự build) bằng MOK (Machine Owner Key,mokutil --import). SBAT là cơ chế thu hồi theo thế hệ (generation) để chặn các bản GRUB cũ có lỗ hổng. - Sản phẩm tự quản lý key: ghi key của nhà sản xuất vào
PK/KEK/dbcủa firmware, ký trực tiếp image GRUB standalone bằngsbsign, không cần shim.
GRUB còn có cơ chế verify riêng bằng GPG (--pubkey khi tạo image, set check_signatures=enforce), yêu cầu mọi file nạp vào (kernel, initrd, grub.cfg) có file chữ ký .sig đi kèm.
Lỗ hổng GRUB
GRUB parse nhiều định dạng (filesystem, font, ảnh) nên đã có nhiều CVE nghiêm trọng, ví dụ BootHole (CVE-2020-10713). Với sản phẩm có Secure Boot, nên theo dõi CVE của GRUB và shim như với kernel. Xem CVE.
References¶
- GNU GRUB Manual - gnu.org
- GRUB - ArchWiki - wiki.archlinux.org
- Unified Extensible Firmware Interface/Secure Boot - ArchWiki - wiki.archlinux.org
- Boot Loader Specification - uapi-group.org
- UEFI on U-Boot - docs.u-boot.org