Giới thiệu chung về các giai đoạn khởi động trong ATF
Trong hệ thống ARMv8, quá trình khởi động được chia thành nhiều giai đoạn rõ ràng nhằm đảm bảo tính an toàn và kiểm soát quyền truy cập tài nguyên. ARM Trusted Firmware (ATF) định nghĩa một chuỗi các giai đoạn tiêu chuẩn: BL1, BL2, BL31, BL32 và BL33. Mỗi giai đoạn đảm nhận vai trò riêng trong việc thiết lập môi trường tin cậy trước khi bàn giao quyền điều khiển cho phần mềm tiếp theo.
- BL1 (Boot ROM): Giai đoạn đầu tiên, chạy ở EL3, đóng vai trò là gốc tin cậy (Root of Trust). Được lưu trữ trong bộ nhớ chỉ đọc tích hợp trên chip, không thể thay đổi sau khi sản xuất.
- BL2 (Trusted Bootloader): Được nạp bởi BL1, khởi động trong SRAM nội bộ, chịu trách nhiệm khởi tạo DRAM và nạp các hình ảnh tin cậy tiếp theo.
- BL31 (EL3 Runtime Firmware): Còn gọi là Secure Monitor, quản lý việc chuyển đổi giữa thế giới Tin cậy (Secure World) và Không tin cậy (Non-secure World) thông qua lệnh SMC.
- BL32 (Secure-EL1 Payload): Thường là hệ điều hành TEE như OP-TEE hoặc TF-A Test Secure Payload.
- BL33 (Non-trusted Firmware): Thông thường là U-Boot, cuối cùng sẽ tải và khởi động kernel Linux ở chế độ Non-secure.
Phân tích sâu giai đoạn BL1
BL1, hay còn gọi là Trusted ROM, là điểm bắt đầu của toàn bộ hệ thống. Mã nguồn chính của nó nằm tại /atf/bl1/aarch64/bl1.ld.S, nơi định nghĩa cấu trúc bộ nhớ, phân vùng mã và dữ liệu, cũng như điểm vào đầu tiên của hệ thống — hàm bl1_entrypoint.
ENTRY(bl1_entrypoint)
Hàm này khởi động bằng cách gọi macro el3_entrypoint_common, tiến hành thiết lập trạng thái ban đầu cho CPU ở mức đặc quyền EL3.
Khởi tạo trạng thái hệ thống tại EL3
Hàm el3_entrypoint_common (định nghĩa tại include/arch/aarch64/el3_common_macros.S) thực hiện loạt công việc nền tảng:
- Thiết lập thanh ghi SCTLR_EL3: Khởi tạo bộ điều khiển hệ thống để vô hiệu hóa các tính năng như lỗi căn chỉnh (
SCTLR_A_BIT), kiểm tra ngăn xếp (SCTLR_SA_BIT), thực thi từ vùng ghi (SCTLR_WXN_BIT) và đặt chế độ endian. Việc đồng bộ hóa bằng lệnhisbđảm bảo thay đổi có hiệu lực ngay lập tức. - Xác định loại khởi động: Kiểm tra xem đây là khởi động lạnh (cold boot) hay nóng (warm boot) thông qua
plat_get_my_entrypoint. Nếu địa chỉ trả về hợp lệ, CPU sẽ nhảy thẳng đến vị trí đó thay vì thực hiện lại toàn bộ quy trình khởi tạo. - Xử lý CPU phụ: Trong hệ thống đa lõi, chỉ CPU chính mới thực hiện đầy đủ quy trình khởi tạo. Các CPU phụ sẽ chờ ở trạng thái WFE cho đến khi được đánh thức bởi CPU chính thông qua cơ chế PSCI.
- Khởi tạo bộ nhớ tạm thời: Cấp phát vùng nhớ tin cậy (Trusted SRAM) dùng cho các biến toàn cục và ngăn xếp trước khi DRAM sẵn sàng.
- Chuẩn bị môi trường C: Sao chép đoạn dữ liệu khả ghi từ ROM sang RAM, xóa đoạn BSS, thiết lập con trỏ ngăn xếp (
sp) và gọiplat_set_my_stackđể định vị ngăn xếp hiện tại. - Cài đặt bảng vector ngoại lệ: Gán địa chỉ bảng vector của BL1 vào thanh ghi
vbar_el3, cho phép xử lý các yêu cầu SMC từ các tầng phần mềm thấp hơn. - Xử lý PIE (Position Independent Executable): Mặc dù hỗ trợ, tính năng này thường bị tắt do chi phí hiệu năng. Khi bật, nó cho phép hình ảnh thực thi ở bất kỳ địa chỉ nào trong không gian ảo.
Thiết lập nền tảng: bl1_setup
Hàm bl1_setup gọi hai hàm nền tảng quan trọng:
bl1_early_platform_setup(): Thực hiện khởi tạo sớm như kích hoạt watchdog, khởi tạo cổng nối tiếp để in log debug, và thiết lập vùng nhớ SRAM dành riêng cho môi trường tin cậy.bl1_plat_arch_setup(): Thiết lập bảng trang MMU cho phép ánh xạ ảo – vật lý, giúp BL1 truy cập an toàn vào các vùng bộ nhớ hệ thống. Trên các nền tảng sử dụng TZC hoặc TZASC, bước này cũng phân vùng DRAM giữa Secure và Non-secure world.
Tải và xác thực BL2
Sau khi hoàn tất khởi tạo, BL1 tiến hành nạp hình ảnh BL2 thông qua hàm bl1_main. Quy trình bao gồm:
- Gọi
bl1_load_bl2()để lấy mô tả hình ảnh từbl1_plat_get_image_desc(BL2_IMAGE_ID). - Kiểm tra điều kiện tiền xử lý (ví dụ: giải nén nếu cần).
- Sử dụng
load_auth_image()để đọc dữ liệu từ thiết bị lưu trữ (eMMC, NOR flash…) vào vùng SRAM an toàn. - Thực hiện xác thực chữ ký (nếu bật tính năng secure boot) thông qua module
auth_mod_init(). - Gọi
bl1_plat_handle_post_image_load()để cập nhật thông tin bộ nhớ có sẵn cho BL2.
static void bl1_load_bl2(void) {
image_desc_t *desc = bl1_plat_get_image_desc(BL2_IMAGE_ID);
image_info_t *info = &desc->image_info;
INFO("BL1: Loading BL2\n");
int err = load_auth_image(BL2_IMAGE_ID, info);
if (err) {
ERROR("Failed to load BL2 firmware.\n");
plat_error_handler(err);
}
err = bl1_plat_handle_post_image_load(BL2_IMAGE_ID);
if (err) {
ERROR("Post-load handling failed for BL2 (%d)\n", err);
plat_error_handler(err);
}
}
Chuẩn bị ngữ cảnh chuyển giao
Trước khi chuyển quyền điều khiển, BL1 phải điền đầy đủ thông tin vào cấu trúc ngữ cảnh CPU — nơi chứa giá trị thanh ghi, trạng thái bảo mật và điểm vào đích. Hàm bl1_prepare_next_image() thực hiện nhiệm vụ này:
- Lấy thông tin điểm vào (
ep_info) của BL2. - Xác định trạng thái bảo mật (Secure/Non-secure) của BL2.
- Thiết lập mức đặc quyền đích (thường là EL2 nếu hỗ trợ).
- Tạo giá trị SPSR phù hợp với chế độ AArch64 và vô hiệu hóa toàn bộ ngắt.
- Điền các thanh ghi X0–X7 để truyền tham số cho BL2 (ví dụ: thông tin bộ nhớ, cấu hình platform).
- Gọi
cm_prepare_el3_exit()để sao chép ngữ cảnh đã chuẩn bị vào thanh ghi phần cứng.
Chuyển giao quyền điều khiển: el3_exit
Hàm el3_exit thực hiện bước cuối cùng — chuyển sang BL2 bằng lệnh eret. Nó thực hiện:
- Lưu con trỏ ngăn xếp hiện tại (
sp) vào ngữ cảnh EL3. - Tải lại các thanh ghi điều khiển:
scr_el3(quyết định hành vi khi thoát),spsr_el3(trạng thái xử lý đích),elr_el3(địa chỉ điểm vào BL2). - Phục hồi các thanh ghi chung (bao gồm
x30– con trỏ trở về). - Thực thi
eret, khiến CPU thoát khỏi EL3 và bắt đầu thực thi tại địa chỉ trongelr_el3.
func el3_exit
mov x17, sp
msr spsel, #1
str x17, [sp, #CTX_RUNTIME_SP]
get_per_world_context x9
ldp x19, x20, [x9, #CTX_CPTR_EL3]
msr cptr_el3, x19
ldr x18, [sp, #CTX_SCR_EL3]
ldp x16, x17, [sp, #CTX_SPSR_EL3]
msr scr_el3, x18
msr spsr_el3, x16
msr elr_el3, x17
bl restore_gp_pmcr_pauth_regs
ldr x30, [sp, #CTX_GPREG_LR]
eret
endfunc
Tổng kết vai trò của BL1
BL1 đóng vai trò then chốt trong việc thiết lập hệ thống tin cậy. Các nhiệm vụ chính bao gồm:
- Xác định đường dẫn khởi động (lạnh/nóng).
- Khởi tạo kiến trúc: vector ngoại lệ, thanh ghi hệ thống (SCTLR, SCR, CPTR).
- Khởi tạo phần cứng nền: console, watchdog, liên kết cụm CPU.
- Thiết lập MMU và vùng nhớ tạm thời.
- Tải, xác thực và nạp hình ảnh BL2 vào SRAM.
- Chuẩn bị ngữ cảnh và chuyển quyền điều khiển sang BL2 ở mức đặc quyền thấp hơn.