Vietnamese (machine translation)

Lưu ý

Mục đích của file này là để độc giả tiếng Việt có thể đọc và hiểu tài liệu nhân kernel dễ dàng hơn, không phải để tạo ra một nhánh tài liệu riêng. Nếu bạn có bất kỳ nhận xét hoặc cập nhật nào cho file này, vui lòng thử cập nhật file tiếng Anh gốc trước. Nếu bạn thấy có sự khác biệt giữa bản dịch và bản gốc, hoặc có vấn đề về bản dịch, vui lòng gửi góp ý hoặc patch cho người dịch của file này, hoặc nhờ người bảo trì và người review tài liệu tiếng Việt giúp đỡ.

Bản gốc:

The Linux/x86 Boot Protocol

Người dịch:

Google Translate (machine translation)

Phiên bản gốc:

8541d8f725c6

Cảnh báo

Tài liệu này được dịch tự động bằng máy và chưa được review bởi người dịch. Nội dung có thể không chính xác hoặc khó hiểu ở một số chỗ. Khi có sự khác biệt với bản gốc, bản gốc luôn là chuẩn. Bản dịch chất lượng cao (được review) được đặt trong thư mục vi_VN/.

1. Giao thức khởi động Linux/x86

Trên nền x86, nhân Linux sử dụng cách khởi động khá phức tạp quy ước. Điều này đã phát triển một phần do các khía cạnh lịch sử, như cũng như mong muốn trong những ngày đầu có được hạt nhân hình ảnh có thể khởi động, mô hình bộ nhớ PC phức tạp và do thay đổi những kỳ vọng trong ngành công nghiệp PC gây ra bởi sự sụp đổ của DOS ở chế độ thực như một hệ điều hành chính thống.

Hiện tại, các phiên bản sau của giao thức khởi động Linux/x86 đã tồn tại.

Lưu ý

The protocol version number should be changed only if the setup header is changed. There is no need to update the version number if boot_params or kernel_info are changed. Additionally, it is recommended to use xloadflags (in this case the protocol version number should not be updated either) or kernel_info to communicate supported Linux kernel features to the boot loader. Due to very limited space available in the original setup header every update to it should be considered with great care. Starting from the protocol 2.15 the primary way to communicate things to the boot loader is the kernel_info.

1.1. Bố cục bộ nhớ

Bản đồ bộ nhớ truyền thống cho trình tải hạt nhân, được sử dụng cho Hình ảnh hoặc Hạt nhân zImage, thường trông giống như:

ZZ0000ZZ
0A0000 +------------------------+

ZZ0001ZZ Không sử dụng. Dành riêng cho BIOS EBDA.

09A000 +------------------------+

ZZ0002ZZ ZZ0003ZZ Để sử dụng bởi mã chế độ thực hạt nhân.

098000 +---------------------------------------

ZZ0004ZZ Mã chế độ thực của hạt nhân.

090200 +---------------------------------------

ZZ0005ZZ Khu vực khởi động kế thừa kernel.

090000 +---------------------------------------

ZZ0006ZZ Phần lớn hình ảnh hạt nhân.

010000 +------------------------------ +

ZZ0007ZZ <- Điểm vào khu vực khởi động 0000:7C00

001000 +------------------------------ +

ZZ0008ZZ

000800 +---------------+

ZZ0009ZZ

000600 +------------------------+

ZZ0010ZZ

000000 +------------------------+

Khi sử dụng bzImage, kernel ở chế độ được bảo vệ đã được chuyển sang 0x100000 (“bộ nhớ cao”) và khối chế độ thực kernel (khu vực khởi động, thiết lập và ngăn xếp/đống) đã được thực hiện có thể định vị lại tới bất kỳ địa chỉ nào giữa 0x10000 và hết bộ nhớ thấp. Unfortunately, in protocols 2.00 and 2.01, phạm vi bộ nhớ 0x90000+ vẫn được kernel sử dụng nội bộ; giao thức 2.02 giải quyết vấn đề đó.

Nên giữ nguyên “mức trần ký ức” - điểm cao nhất trong bộ tải khởi động chạm vào bộ nhớ thấp -- càng thấp càng tốt, vì một số BIOS mới hơn đã bắt đầu phân bổ một lượng khá lớn bộ nhớ, được gọi là Vùng dữ liệu BIOS mở rộng, gần đỉnh thấp trí nhớ. Bộ tải khởi động nên sử dụng lệnh gọi BIOS “INT 12h” để xác minh bộ nhớ còn trống bao nhiêu.

Thật không may, nếu INT 12h báo cáo rằng dung lượng bộ nhớ quá lớn thấp, bộ tải khởi động thường không thể làm gì khác ngoài việc báo cáo một lỗi cho người dùng. Do đó bộ nạp khởi động phải được thiết kế để chiếm ít không gian trong bộ nhớ thấp nhất có thể. cho zImage hoặc hạt nhân bzImage cũ, cần dữ liệu được ghi vào 0x90000, bộ tải khởi động phải đảm bảo không sử dụng bộ nhớ trên điểm 0x9A000; quá nhiều BIOS sẽ vượt quá điểm đó.

Đối với hạt nhân bzImage hiện đại có phiên bản giao thức khởi động >= 2.02, bố trí bộ nhớ như sau được đề xuất

~ ~

ZZ0000ZZ

100000 +------------------------+

ZZ0001ZZ

0A0000 +------------------------+

ZZ0002ZZ Để càng nhiều càng tốt không sử dụng ~ ~ ZZ0003ZZ (Cũng có thể dưới mốc X+10000)

X+10000 +---------------+

ZZ0004ZZ Để sử dụng bởi mã chế độ thực hạt nhân.

X+08000 +---------------+

ZZ0005ZZ Mã chế độ thực của hạt nhân. ZZ0006ZZ Khu vực khởi động kế thừa kernel.

X +---------------+

ZZ0007ZZ <- Điểm vào khu vực khởi động 0000:7C00

001000 +------------------------------ +

ZZ0008ZZ

000800 +---------------+

ZZ0009ZZ

000600 +------------------------+

ZZ0010ZZ

000000 +------------------------+

... where the address X is as low as the design of the boot loader permits.

1.2. Tiêu đề hạt nhân chế độ thực

Trong văn bản sau đây và bất kỳ vị trí nào trong chuỗi khởi động kernel, “a ngành” đề cập đến 512 byte. Nó độc lập với khu vực thực tế kích thước của phương tiện cơ bản.

Bước đầu tiên trong việc tải nhân Linux là tải mã chế độ thực (khu vực khởi động và mã thiết lập) và sau đó kiểm tra tiêu đề sau ở offset 0x01f1. Mã chế độ thực có thể tổng cộng lên tới 32K, mặc dù bộ tải khởi động có thể chọn chỉ tải hai phần đầu tiên các lĩnh vực (1K) và sau đó kiểm tra kích thước của khu vực khởi động.

Tiêu đề trông giống như:

Lưu ý

  1. For backwards compatibility, if the setup_sects field contains 0, the real value is 4.

  2. For boot protocol prior to 2.04, the upper two bytes of the syssize field are unusable, which means the size of a bzImage kernel cannot be determined.

  3. Ignored, but safe to set, for boot protocols 2.02-2.09.

Nếu không tìm thấy số ma thuật “HdrS” (0x53726448) ở offset 0x202, phiên bản giao thức khởi động là “cũ”. Đang tải một kernel cũ, cần giả sử các tham số sau:

Loại hình ảnh = zImage

initrd không được hỗ trợ Hạt nhân chế độ thực phải được đặt ở 0x90000.

Mặt khác, trường “phiên bản” chứa phiên bản giao thức, ví dụ: giao thức phiên bản 2.01 sẽ chứa 0x0201 trong trường này. Khi nào cài đặt các trường trong tiêu đề, bạn phải đảm bảo chỉ đặt các trường được hỗ trợ bởi phiên bản giao thức đang sử dụng.

1.3. Chi tiết về trường tiêu đề

Đối với mỗi trường, một số thông tin từ kernel đến bootloader (“đọc”), một số dự kiến sẽ được bộ nạp khởi động điền vào (“viết”), và một số dự kiến sẽ được đọc và sửa đổi bởi bộ nạp khởi động (“sửa đổi”).

Tất cả các bộ tải khởi động cho mục đích chung phải ghi vào các trường được đánh dấu (bắt buộc). Những người tải khởi động muốn tải kernel cùng một lúc địa chỉ không chuẩn phải điền vào các trường được đánh dấu (reloc); khác bộ tải khởi động có thể bỏ qua các trường đó.

Thứ tự byte của tất cả các trường là little endian (x86 là x86).

Độ lệch/kích thước: 0x1f1/1 Giao thức: ALL ========================

Kích thước của mã thiết lập trong các cung 512 byte. Nếu trường này là

0, giá trị thực là 4. Mã chế độ thực bao gồm mã khởi động khu vực (luôn luôn có một khu vực 512 byte) cộng với mã thiết lập.

Độ lệch/kích thước: 0x1f2/2 Giao thức: ALL ===============================

Nếu trường này khác 0 thì gốc mặc định là chỉ đọc. Việc sử dụng

trường này không được dùng nữa; sử dụng tùy chọn “ro” hoặc “rw” trên thay vào đó là dòng lệnh.

Độ lệch/kích thước: 0x1f4/4 (giao thức 2.04+) 0x1f4/2 (giao thức ALL) Giao thức: 2.04+ ===============================================================

Kích thước của mã chế độ được bảo vệ theo đơn vị đoạn văn 16 byte.

Đối với các phiên bản giao thức cũ hơn 2.04, trường này chỉ có hai byte rộng và do đó không thể tin cậy được đối với kích thước của hạt nhân nếu cờ LOAD_HIGH được đặt.

Độ lệch/kích thước: 0x1f8/2 Giao thức: ALL ============================

Lĩnh vực này đã lỗi thời.

1.4. Độ lệch/kích thước: 0x1fa/2

Vui lòng xem phần trên SPECIAL COMMAND LINE OPTIONS.

Độ lệch/kích thước: 0x1fc/2 Giao thức: ALL ===============================

Số thiết bị gốc mặc định. Việc sử dụng trường này là

không được dùng nữa, thay vào đó hãy sử dụng tùy chọn “root=” trên dòng lệnh.

Độ lệch/kích thước: 0x1fe/2 Giao thức: ALL ======================

Chứa 0xAA55. Đây là thứ gần gũi nhất mà nhân Linux cũ có

đến một con số kỳ diệu.

Chứa lệnh nhảy x86, 0xEB theo sau là phần bù đã ký

liên quan đến byte 0x202. Điều này có thể được sử dụng để xác định kích thước của tiêu đề.

Chứa số ma thuật “HdrS” (0x53726448).

Chứa phiên bản giao thức khởi động, ở định dạng (chính << 8) + phụ,

ví dụ: 0x0204 cho phiên bản 2.04 và 0x0a11 cho phiên bản giả định 17/10.

Độ lệch/kích thước: 0x208/4 Giao thức: 2,00+ ===============================

Móc bộ tải khởi động (xem ADVANCED BOOT LOADER HOOKS bên dưới.)

Phân đoạn tải thấp (0x1000). Lỗi thời.

Nếu được đặt thành giá trị khác 0, chứa một con trỏ tới NUL đã kết thúc

Chuỗi số phiên bản kernel mà con người có thể đọc được, nhỏ hơn 0x200. Điều này có thể được sử dụng để hiển thị phiên bản kernel cho người dùng. Giá trị này phải nhỏ hơn (0x200 * setup_sects).

Ví dụ: nếu giá trị này được đặt thành 0x1c00, phiên bản kernel

chuỗi số có thể được tìm thấy ở offset 0x1e00 trong tệp kernel. Đây là giá trị hợp lệ khi và chỉ khi trường “setup_sects” chứa giá trị 15 hoặc cao hơn, như:

0x1c00 < 15 * 0x200 (= 0x1e00) nhưng

0x1c00 >= 14 * 0x200 (= 0x1c00)

0x1c00 >> 9 = 14, Vì vậy giá trị tối thiểu cho setup_secs là 15.

Độ lệch/kích thước: 0x210/1 Giao thức: 2,00+ ================================

Nếu bộ tải khởi động của bạn có ID được chỉ định (xem bảng bên dưới), hãy nhập

0xTV ở đây, trong đó T là mã định danh cho bộ tải khởi động và V là một số phiên bản. Nếu không, hãy nhập 0xFF vào đây.

Đối với ID bộ tải khởi động trên T = 0xD, hãy ghi T = 0xE vào trường này và

ghi ID mở rộng trừ 0x10 vào trường ext_loader_type. Tương tự, trường ext_loader_ver có thể được sử dụng để cung cấp nhiều hơn bốn bit cho phiên bản bootloader.

Ví dụ: với T = 0x15, V = 0x234, hãy viết:

loại_of_loader <- 0xE4

ext_loader_type <- 0x05 ext_loader_ver <- 0x23

ID bộ tải khởi động được chỉ định:

Tên trường: Loadflags Loại: sửa đổi (bắt buộc) Độ lệch/kích thước: 0x211/1 Giao thức: 2,00+ =================================

Trường này là một bitmask.

Bit 0 (đọc): LOADED_HIGH

  • Nếu 0, mã chế độ bảo vệ được tải ở mức 0x10000.
    • Nếu 1, mã chế độ bảo vệ được tải ở mức 0x100000.

Bit 1 (nhân bên trong): KASLR_FLAG

  • Được sử dụng nội bộ bởi hạt nhân nén để giao tiếp

    Trạng thái KASLR thành kernel thích hợp.

  • Nếu là 1, KASLR đã được bật.
    • Nếu 0, KASLR bị vô hiệu hóa.

Bit 5 (ghi): QUIET_FLAG

  • Nếu 0 thì in tin nhắn sớm.
    • Nếu 1, ngăn chặn các tin nhắn sớm.

Điều này yêu cầu kernel (bộ giải nén và đầu

kernel) để không viết các tin nhắn sớm yêu cầu truy cập trực tiếp vào phần cứng hiển thị.

Bit 6 (lỗi thời): KEEP_SEGMENTS

Giao thức: 2.07+

  • Lá cờ này đã lỗi thời.

Bit 7 (ghi): CAN_USE_HEAP

Đặt bit này thành 1 để chỉ ra rằng giá trị được nhập vào

heap_end_ptr là hợp lệ. Nếu trường này trống, một số mã thiết lập chức năng sẽ bị vô hiệu hóa.

Độ lệch/kích thước: 0x212/2 Giao thức: 2,00-2,01 =================================

Khi sử dụng giao thức 2.00 hoặc 2.01, nếu kernel chế độ thực không có

được tải ở 0x90000, nó sẽ được chuyển đến đó sau trong quá trình tải trình tự. Điền vào trường này nếu bạn muốn có thêm dữ liệu (chẳng hạn như dòng lệnh kernel) được di chuyển cùng với kernel chế độ thực chính nó.

Đơn vị là byte bắt đầu từ phần đầu của khu vực khởi động.

Trường này có thể bị bỏ qua khi giao thức là 2.02 trở lên hoặc

nếu mã chế độ thực được tải ở 0x90000.

Độ lệch/kích thước: 0x214/4 Giao thức: 2,00+ =======================================

Địa chỉ để nhảy tới ở chế độ được bảo vệ. Điều này mặc định cho tải

địa chỉ của kernel và có thể được bộ tải khởi động sử dụng để xác định địa chỉ tải thích hợp.

Trường này có thể được sửa đổi cho hai mục đích:

  1. như một hook bộ tải khởi động (xem Móc bộ tải khởi động nâng cao bên dưới.)

  2. nếu bộ nạp khởi động không cài đặt hook sẽ tải một

    hạt nhân có thể định vị lại ở một địa chỉ không chuẩn, nó sẽ phải sửa đổi trường này để trỏ đến địa chỉ tải.

Độ lệch/kích thước: 0x218/4 Giao thức: 2,00+ ================================

Địa chỉ tuyến tính 32 bit của ramdisk hoặc ramf ban đầu. Khởi hành lúc

0 nếu không có ramdisk/ramfs ban đầu.

Độ lệch/kích thước: 0x21c/4 Giao thức: 2,00+ ================================

Kích thước của ramdisk hoặc ramf ban đầu. Để lại ở mức 0 nếu không có

ramdisk/ramfs ban đầu.

Độ lệch/kích thước: 0x220/4 Giao thức: 2,00+ ============================

Lĩnh vực này đã lỗi thời.

Độ lệch/kích thước: 0x224/2 Giao thức: 2.01+ ================================

Đặt trường này thành phần bù (từ đầu chế độ thực

code) ở cuối ngăn xếp/đống thiết lập, trừ 0x0200.

Độ lệch/kích thước: 0x226/1 Giao thức: 2.02+ ==============================

Trường này được sử dụng như phần mở rộng của số phiên bản trong

trường type_of_loader. Tổng số phiên bản được coi là (type_of_loader & 0x0f) + (ext_loader_ver << 4).

Việc sử dụng trường này là dành riêng cho bộ tải khởi động. Nếu không được viết thì nó

là số không.

Hạt nhân trước 2.6.31 không nhận ra trường này, nhưng nó an toàn

để viết cho giao thức phiên bản 2.02 trở lên.

Độ lệch/kích thước: 0x227/1 Giao thức: 2.02+ ======================================================================

Trường này được sử dụng như phần mở rộng của số loại trong

trường type_of_loader. Nếu loại trong type_of_loader là 0xE thì loại thực tế là (ext_loader_type + 0x10).

Trường này bị bỏ qua nếu loại trong type_of_loader không phải là 0xE.

Hạt nhân trước 2.6.31 không nhận ra trường này, nhưng nó an toàn

để viết cho giao thức phiên bản 2.02 trở lên.

Độ lệch/kích thước: 0x228/4 Giao thức: 2.02+ ================================

Đặt trường này thành địa chỉ tuyến tính của dòng lệnh kernel.

Dòng lệnh kernel có thể được đặt ở bất cứ đâu giữa phần cuối của vùng thiết lập và 0xA0000; nó không nhất thiết phải nằm ở cùng phân đoạn 64K với chính mã chế độ thực.

Điền vào trường này ngay cả khi bộ tải khởi động của bạn không hỗ trợ

dòng lệnh, trong trường hợp đó bạn có thể trỏ dòng lệnh này tới một chuỗi trống (hoặc tốt hơn nữa là chuỗi “auto”.) Nếu trường này được để lại ở 0, kernel sẽ cho rằng bộ tải khởi động của bạn không hỗ trợ giao thức 2.02+.

Độ lệch/kích thước: 0x22c/4 Giao thức: 2.03+ ============================

Địa chỉ tối đa có thể bị chiếm giữ bởi địa chỉ ban đầu

nội dung ramdisk/ramfs. Đối với các giao thức khởi động 2.02 hoặc cũ hơn, điều này trường không có mặt và địa chỉ tối đa là 0x37FFFFFF. (Cái này địa chỉ được định nghĩa là địa chỉ của byte an toàn cao nhất, vì vậy nếu ramdisk của bạn dài chính xác là 131072 byte và trường này là 0x37FFFFFF, bạn có thể khởi động đĩa RAM của mình ở 0x37FE0000.)

Độ lệch/kích thước: 0x230/4 Giao thức: 2.05+ (đọc), 2.10+ (sửa đổi) ===========================================

Đơn vị căn chỉnh được hạt nhân yêu cầu (nếu relocatable_kernel là

đúng.) Một hạt nhân có thể định vị lại được tải theo căn chỉnh không tương thích với giá trị trong trường này sẽ được sắp xếp lại trong quá trình khởi tạo kernel.

Bắt đầu với giao thức phiên bản 2.10, điều này phản ánh kernel

căn chỉnh ưu tiên để có hiệu suất tối ưu; nó có thể cho bộ tải để sửa đổi trường này để cho phép căn chỉnh ít hơn. Xem trường min_alignment và pref_address bên dưới.

Độ lệch/kích thước: 0x234/1 Giao thức: 2.05+ ================================

Nếu trường này khác 0 thì phần chế độ được bảo vệ của hạt nhân có thể

được tải tại bất kỳ địa chỉ nào thỏa mãn trường kernel_alignment. Sau khi tải, bộ tải khởi động phải đặt trường code32_start thành trỏ đến mã được tải hoặc tới hook bộ tải khởi động.

Trường này, nếu khác 0, biểu thị lũy thừa của hai giá trị tối thiểu

kernel cần căn chỉnh, trái ngược với mức ưu tiên, để khởi động. Nếu bộ tải khởi động sử dụng trường này, nó sẽ cập nhật trường kernel_alignment với đơn vị căn chỉnh mong muốn; tiêu biểu:

kernel_alignment = 1 << min_alignment;

Có thể có chi phí thực hiện đáng kể nếu sử dụng quá mức

hạt nhân bị lệch. Vì vậy, trình tải thường phải thử từng căn chỉnh lũy thừa hai từ kernel_alignment xuống căn chỉnh này.

Độ lệch/kích thước: 0x236/2 Giao thức: 2.12+ =======================

Trường này là một bitmask.

Bit 0 (đọc): XLF_KERNEL_64

  • Nếu là 1, kernel này có điểm vào 64-bit kế thừa là 0x200.

Bit 1 (đọc): XLF_CAN_BE_LOADED_ABOVE_4G

  • Nếu là 1 thì kernel/boot_params/cmdline/ramdisk có thể trên 4G.

Bit 2 (đọc): XLF_EFI_HANDOVER_32

  • Nếu 1, kernel hỗ trợ điểm vào chuyển giao EFI 32-bit

    được đưa ra tại thời điểm chuyển giao_offset.

Bit 3 (đọc): XLF_EFI_HANDOVER_64

  • Nếu 1, kernel hỗ trợ điểm vào chuyển giao EFI 64-bit

    đưa ra tại handover_offset + 0x200.

Bit 4 (đọc): XLF_EFI_KEXEC

  • Nếu 1, kernel hỗ trợ khởi động kexec EFI với hỗ trợ thời gian chạy EFI.

Kích thước tối đa của dòng lệnh mà không cần kết thúc

không. Điều này có nghĩa là dòng lệnh có thể chứa tối đa ký tự cmdline_size. Với giao thức phiên bản 2.05 trở về trước, kích thước tối đa là 255.

Độ lệch/kích thước: 0x23c/4 Giao thức: 2.07+ ====================================================

Trong môi trường ảo hóa, kiến trúc phần cứng cấp thấp

các phần như xử lý ngắt, xử lý bảng trang và việc truy cập các thanh ghi điều khiển quá trình cần phải được thực hiện khác nhau.

Trường này cho phép bộ nạp khởi động thông báo cho kernel biết chúng ta đang ở trong một

một trong những môi trường đó

0x00000002 Xen 0x00000003 Intel MID (Moorestown, CloverTrail, Merrifield, Moorefield) Nền tảng TV 0x00000004 CE4100 ===========================================

Độ lệch/kích thước: 0x240/8 Giao thức: 2.07+ ========================================

Một con trỏ tới dữ liệu dành riêng cho phân mục phần cứng

Trường này hiện không được sử dụng cho môi trường x86/PC mặc định, không sửa đổi.

Nếu khác 0 thì trường này chứa phần bù từ đầu

của mã chế độ được bảo vệ vào tải trọng.

Tải trọng có thể được nén. Định dạng của cả tệp nén và

dữ liệu không nén phải được xác định bằng phép thuật tiêu chuẩn những con số. Các định dạng nén hiện được hỗ trợ là gzip (số ma thuật 1F 8B hoặc 1F 9E), bzip2 (con số ma thuật 42 5A), LZMA (con số ma thuật 5D 00), XX (con số ma thuật FD 37), LZ4 (con số ma thuật 02 21) và ZSTD (con số kỳ diệu 28 B5). Tải trọng không nén là hiện tại luôn là ELF (con số kỳ diệu 7F 45 4C 46).

Chiều dài của tải trọng.

Độ lệch/kích thước: 0x250/8 Giao thức: 2.09+ ============================

Con trỏ vật lý 64 bit tới NULL đã kết thúc danh sách liên kết đơn của

cấu trúc setup_data. Điều này được sử dụng để xác định một khởi động mở rộng hơn cơ chế truyền tham số Định nghĩa của struct setup_data là như sau:

cấu trúc setup_data {

__u64 tiếp theo; __u32 loại; __u32 len; __u8 dữ liệu[];

}

Trong đó, tiếp theo là con trỏ vật lý 64 bit tới nút tiếp theo của

danh sách liên kết, trường tiếp theo của nút cuối cùng là 0; loại được sử dụng để xác định nội dung của dữ liệu; len là độ dài của dữ liệu lĩnh vực; dữ liệu chứa tải trọng thực sự.

Danh sách này có thể được sửa đổi tại một số điểm trong quá trình khởi động

quá trình. Vì vậy, khi sửa đổi danh sách này, người ta phải luôn thực hiện chắc chắn xem xét trường hợp danh sách liên kết đã chứa mục nhập.

setup_data hơi khó sử dụng cho các đối tượng dữ liệu cực lớn,

cả hai vì tiêu đề setup_data phải liền kề với đối tượng dữ liệu và bởi vì nó có trường có độ dài 32 bit. Tuy nhiên, điều quan trọng là các giai đoạn trung gian của quá trình khởi động có cách để xác định khối bộ nhớ bị chiếm giữ bởi dữ liệu kernel.

Do đó, cấu trúc setup_indirect và loại SETUP_INDIRECT đã được giới thiệu trong

giao thức 2.15:

cấu trúc setup_indirect {

__u32 loại; __u32 dành riêng; /* Dành riêng, phải được đặt thành 0. */ __u64 len; __u64 địa chỉ;

};

Thành viên loại là SETUP_INDIRECT | Loại SETUP_*. Tuy nhiên, nó không thể

Bản thân SETUP_INDIRECT kể từ khi tạo cấu trúc cây setup_indirect có thể cần nhiều không gian ngăn xếp trong thứ gì đó cần phân tích cú pháp và không gian ngăn xếp có thể bị giới hạn trong bối cảnh khởi động.

Hãy đưa ra một ví dụ về cách trỏ đến dữ liệu SETUP_E820_EXT bằng setup_indirect.

Trong trường hợp này setup_data và setup_indirect sẽ trông như thế này

cấu trúc setup_data {

.next = 0, /* hoặc <addr_of_next_setup_data_struct> */ .type = SETUP_INDIRECT, .len = sizeof(setup_indirect), .data[sizeof(setup_indirect)] = (struct setup_indirect) {

.type = SETUP_INDIRECT | SETUP_E820_EXT, .reserved = 0, .len = <len_of_SETUP_E820_EXT_data>, .addr = <addr_of_SETUP_E820_EXT_data>,

},

}

Lưu ý

SETUP_INDIRECT | SETUP_NONE objects cannot be properly distinguished from SETUP_INDIRECT itself. So, this kind of objects cannot be provided by the bootloaders.

Trường này, nếu khác 0, biểu thị địa chỉ tải ưu tiên cho

hạt nhân. Bộ tải khởi động được định vị lại sẽ cố gắng tải vào thời điểm này địa chỉ nếu có thể.

Một hạt nhân không thể định vị lại sẽ tự di chuyển vô điều kiện và chạy

tại địa chỉ này. Một hạt nhân có khả năng định vị lại sẽ tự di chuyển đến địa chỉ này nếu nó được tải bên dưới địa chỉ này.

Trường này cho biết số lượng bộ nhớ liền kề tuyến tính bắt đầu

tại địa chỉ bắt đầu thời gian chạy kernel mà kernel cần trước nó có khả năng kiểm tra bản đồ bộ nhớ của nó. Đây không phải là điều tương tự là tổng dung lượng bộ nhớ mà kernel cần để khởi động, nhưng nó có thể được sử dụng bởi bộ tải khởi động định vị lại để giúp chọn tải an toàn địa chỉ cho kernel.

Địa chỉ bắt đầu thời gian chạy kernel được xác định bằng thuật toán sau:

if (relocatable_kernel) {
nếu (load_address < pref_address)

Load_address = pref_address;

thời gian chạy_start = căn_up(load_address, kernel_alignment);

} khác {

thời gian chạy_start = pref_address;

}

Do đó, vị trí và kích thước cửa sổ bộ nhớ cần thiết có thể được ước tính bằng một bộ tải khởi động như:

bộ nhớ_window_start = thời gian chạy_start;

bộ nhớ_window_size = init_size;

1.5. Độ lệch/kích thước: 0x264/4

Trường này là phần bù từ đầu của ảnh hạt nhân đến

điểm vào giao thức chuyển giao EFI. Bộ tải khởi động sử dụng EFI giao thức chuyển giao để khởi động kernel sẽ chuyển sang phần bù này.

Xem EFI HANDOVER PROTOCOL bên dưới để biết thêm chi tiết.

Độ lệch/kích thước: 0x268/4 Giao thức: 2.15+ ================================

Trường này là phần bù từ phần đầu của ảnh hạt nhân đến phần

kernel_info. Cấu trúc kernel_info được nhúng trong image Linux trong vùng chế độ bảo vệ không nén.

1.6. Thông tin hạt nhân

Mối quan hệ giữa các tiêu đề tương tự với các dữ liệu khác nhau phần:

setup_header = .data

boot_params/setup_data = .bss

Danh sách trên còn thiếu điều gì? Đúng rồi:

kernel_info = .rodata

Chúng tôi đã (ab) sử dụng .data cho những thứ có thể chuyển sang .rodata hoặc .bss cho một thời gian dài, vì thiếu các lựa chọn thay thế và - đặc biệt là ở giai đoạn đầu - quán tính. Ngoài ra, sơ khai BIOS chịu trách nhiệm tạo boot_params, vì vậy nó không phải có sẵn cho trình tải dựa trên BIOS (mặc dù vậy là setup_data).

setup_header bị giới hạn vĩnh viễn ở 144 byte do phạm vi tiếp cận của Trường nhảy 2 byte, được nhân đôi thành trường độ dài cho cấu trúc, được kết hợp với kích thước của “lỗ” trong struct boot_params mà trình tải chế độ được bảo vệ hoặc sơ khai BIOS phải sao chép nó vào. Hiện tại nó dài 119 byte, để lại cho chúng tôi 25 byte rất quý giá. Đây không phải là thứ có thể sửa được mà không sửa đổi hoàn toàn giao thức khởi động, phá vỡ khả năng tương thích ngược.

boot_params thích hợp được giới hạn ở 4096 byte, nhưng có thể được mở rộng tùy ý bằng cách thêm các mục setup_data. Nó không thể được sử dụng để truyền đạt các thuộc tính của hình ảnh hạt nhân, vì nó là .bss và không có nội dung do hình ảnh cung cấp.

kernel_info giải quyết vấn đề này bằng cách cung cấp một nơi có thể mở rộng thông tin về hình ảnh hạt nhân. Nó ở dạng chỉ đọc, vì kernel không thể dựa vào một bootloader sao chép nội dung của nó ở bất cứ đâu, nhưng không sao; nếu nó trở thành cần thiết, nó vẫn có thể chứa các mục dữ liệu mà bộ tải khởi động được kích hoạt sẽ dự kiến ​​sẽ sao chép vào một đoạn setup_data.

Tất cả dữ liệu kernel_info phải là một phần của cấu trúc này. Dữ liệu có kích thước cố định phải được đặt trước nhãn kernel_info_var_len_data. Dữ liệu kích thước thay đổi phải được đặt sau nhãn kernel_info_var_len_data. Mỗi đoạn dữ liệu có kích thước thay đổi phải được bắt đầu bằng tiêu đề/ma thuật và kích thước của nó, ví dụ:

kernel_info:

.ascii “LToP” /* Tiêu đề, phần trên cùng của Linux (cấu trúc). / .long kernel_info_var_len_data - kernel_info .long kernel_info_end - kernel_info .long 0x01234567 / Một số dữ liệu có kích thước cố định cho bộ nạp khởi động. */

kernel_info_var_len_data: example_struct: /* Một số dữ liệu có kích thước thay đổi cho bộ nạp khởi động. */

.ascii “0123” /* Tiêu đề/Magic. */ .long example_struct_end - example_struct .ascii “Cấu trúc” .dài 0x89012345

example_struct_end: example_strings: /* Một số dữ liệu có kích thước thay đổi cho bộ nạp khởi động. */

.ascii “ABCD” /* Tiêu đề/Magic. */ .long example_strings_end - example_strings .asciz “Chuỗi_0” .asciz “Chuỗi_1”

example_strings_end: kernel_info_end:

Bằng cách này, kernel_info là một blob độc lập.

Lưu ý

Each variable size data header/magic can be any 4-character string, without 0 at the end of the string, which does not collide with existing variable length data headers/magics.

1.7. Chi tiết về các trường kernel_info


Chứa số ma thuật “LToP” (0x506f544c).


Trường này chứa kích thước của kernel_info bao gồm kernel_info.header.

Nó không tính kích thước kernel_info.kernel_info_var_len_data. Trường này nên được được bộ tải khởi động sử dụng để phát hiện các trường có kích thước cố định được hỗ trợ trong kernel_info và bắt đầu kernel_info.kernel_info_var_len_data.


Trường này chứa kích thước của kernel_info bao gồm kernel_info.header

và kernel_info.kernel_info_var_len_data.

Trường này chứa loại tối đa được phép cho các cấu trúc setup_data và setup_indirect.

1.8. Dòng lệnh hạt nhân

Dòng lệnh kernel đã trở thành một cách quan trọng để khởi động bộ nạp để giao tiếp với kernel. Một số tùy chọn của nó cũng có liên quan đến chính bộ tải khởi động, hãy xem “các tùy chọn dòng lệnh đặc biệt” bên dưới.

Dòng lệnh kernel là một chuỗi kết thúc bằng null. Tối đa chiều dài có thể được lấy từ trường cmdline_size. Trước giao thức phiên bản 2.06, tối đa là 255 ký tự. Một chuỗi quá long sẽ được kernel tự động cắt bớt.

Nếu phiên bản giao thức khởi động là 2.02 hoặc mới hơn, địa chỉ của dòng lệnh kernel được cung cấp bởi trường tiêu đề cmd_line_ptr (xem ở trên.) Địa chỉ này có thể ở bất kỳ đâu giữa phần cuối của quá trình thiết lập đống và 0xA0000.

Nếu phiên bản giao thức là ZZ0000ZZ 2.02 trở lên, kernel dòng lệnh được nhập bằng giao thức sau:

  • Tại offset 0x0020(word), “cmd_line_magic”, nhập magic

    số 0xA33F.

  • Tại offset 0x0022(word), “cmd_line_offset”, nhập offset

    của dòng lệnh kernel (so với thời điểm bắt đầu của hạt nhân chế độ thực).

  • Dòng lệnh kernel ZZ0000ZZ nằm trong vùng bộ nhớ

    được bao phủ bởi setup_move_size, vì vậy bạn có thể cần điều chỉnh điều này lĩnh vực.

1.9. Bố cục bộ nhớ của mã chế độ thực

Mã chế độ thực yêu cầu thiết lập ngăn xếp/đống, cũng như bộ nhớ được phân bổ cho dòng lệnh kernel. Điều này cần phải được thực hiện trong bộ nhớ có thể truy cập ở chế độ thực tính bằng megabyte dưới cùng.

Cần lưu ý rằng các máy hiện đại thường có Extended khá lớn. Vùng dữ liệu BIOS (EBDA). Vì vậy, nên sử dụng càng ít có dung lượng megabyte thấp nhất có thể.

Thật không may, trong những trường hợp sau, bộ nhớ 0x90000 phân đoạn phải được sử dụng:

  • Khi tải kernel zImage ((loadflags & 0x01) == 0).
    • Khi tải hạt nhân giao thức khởi động 2.01 hoặc cũ hơn.

Lưu ý

For the 2.00 and 2.01 boot protocols, the real-mode code can be loaded at another address, but it is internally relocated to 0x90000. For the “old” protocol, the real-mode code must be loaded at 0x90000.

Khi tải ở 0x90000, tránh sử dụng bộ nhớ trên 0x9a000.

Đối với giao thức khởi động 2.02 trở lên, dòng lệnh không cần phải nằm trong cùng phân đoạn 64K với mã thiết lập chế độ thực; nó là do đó được phép cung cấp cho ngăn xếp/đống phân đoạn 64K đầy đủ và xác định vị trí dòng lệnh phía trên nó.

Dòng lệnh kernel không được đặt bên dưới chế độ thực mã, nó cũng không nên được đặt trong bộ nhớ cao.

1.10. Cấu hình khởi động mẫu

Là một cấu hình mẫu, giả sử bố cục sau của mạng thực đoạn chế độ.

Khi tải dưới 0x90000, hãy sử dụng toàn bộ phân đoạn:

Khi tải ở 0x90000 HOẶC phiên bản giao thức là 2.01 trở về trước:

Bộ tải khởi động như vậy phải nhập các trường sau vào tiêu đề

base_ptr dài không dấu; /* địa chỉ cơ sở cho phân đoạn chế độ thực */

nếu (setup_sects == 0)

setup_sects = 4;

nếu (giao thức >= 0x0200) {

type_of_loader = <gõ mã>; nếu (loading_initrd) {

ramdisk_image = <initrd_address>; ramdisk_size = <initrd_size>;

}

if (giao thức >= 0x0202 && Loadflags & 0x01)

heap_end = 0xe000;

khác

đống_end = 0x9800;

nếu (giao thức >= 0x0201) {

heap_end_ptr = heap_end - 0x200; cờ tải |= 0x80; /* CAN_USE_HEAP */

}

nếu (giao thức >= 0x0202) {

cmd_line_ptr = base_ptr + heap_end; strcpy(cmd_line_ptr, cmdline);

} khác {

cmd_line_magic = 0xA33F; cmd_line_offset = heap_end; setup_move_size = heap_end + strlen(cmdline) + 1; strcpy(base_ptr + cmd_line_offset, cmdline);

}

} khác {

/* Kernel rất cũ */

đống_end = 0x9800;

cmd_line_magic = 0xA33F;

cmd_line_offset = heap_end;

/* Kernel MUST rất cũ có mã chế độ thực được tải ở 0x90000 */
nếu (base_ptr != 0x90000) {

/* Sao chép kernel chế độ thực / memcpy(0x90000, base_ptr, (setup_sects + 1) * 512); base_ptr = 0x90000; / Đã di dời */

}

strcpy(0x90000 + cmd_line_offset, cmdline);

/* Nên xóa bộ nhớ lên tới mốc 32K */

bộ nhớ (0x90000 + (setup_sects + 1) * 512, 0, (64 - (setup_sects + 1)) * 512);

}

1.11. Đang tải phần còn lại của hạt nhân

Hạt nhân 32 bit (không phải chế độ thực) bắt đầu ở offset (setup_sects + 1) * 512 trong tệp kernel (một lần nữa, nếu setup_sects == 0 thì giá trị thực là 4.) Nó phải được tải tại địa chỉ 0x10000 cho hạt nhân Image/zImage và 0x100000 cho hạt nhân bzImage.

Hạt nhân là hạt nhân bzImage nếu giao thức >= 2.00 và 0x01 bit (LOAD_HIGH) trong trường cờ tải được đặt:

is_bzImage = (giao thức >= 0x0200) && (loadflags & 0x01);

tải_địa chỉ = is_bzImage? 0x100000 : 0x10000;

Lưu ý

Image/zImage kernels can be up to 512K in size, and thus use the entire 0x10000-0x90000 range of memory. This means it is pretty much a requirement for these kernels to load the real-mode part at 0x90000. bzImage kernels allow much more flexibility.

1.12. Tùy chọn dòng lệnh đặc biệt

Nếu dòng lệnh do bộ tải khởi động cung cấp được nhập bởi người dùng, người dùng có thể mong đợi các tùy chọn dòng lệnh sau hoạt động. Thông thường chúng không nên bị xóa khỏi dòng lệnh kernel ngay cả mặc dù không phải tất cả chúng đều thực sự có ý nghĩa đối với kernel. Khởi động tác giả trình tải cần tùy chọn dòng lệnh bổ sung để khởi động bản thân bộ nạp sẽ đăng ký chúng trong The kernel’s command-line parameters để đảm bảo rằng chúng sẽ không xung đột với các tùy chọn kernel thực tế hiện tại hoặc trong tương lai.

vga=<chế độ>

<mode> ở đây là một số nguyên (trong ký hiệu C, hoặc thập phân, bát phân hoặc thập lục phân) hoặc một trong các chuỗi “bình thường” (nghĩa là 0xFFFF), “ext” (nghĩa là 0xFFFE) hoặc “hỏi” (có nghĩa là 0xFFFD). Giá trị này phải được nhập vào trường vid_mode, vì nó được kernel sử dụng trước lệnh dòng được phân tích cú pháp.

ghi nhớ=<kích thước>

<size> là một số nguyên trong ký hiệu C, theo sau là tùy chọn (không phân biệt chữ hoa chữ thường) K, M, G, T, P hoặc E (nghĩa là << 10, << 20, << 30, << 40, << 50 hoặc << 60). Điều này xác định sự kết thúc của bộ nhớ vào kernel. Điều này ảnh hưởng đến vị trí có thể của một initrd, vì initrd nên được đặt gần cuối trí nhớ. Lưu ý rằng đây là một tùy chọn cho hạt nhân ZZ0000ZZ và bộ nạp khởi động!

initrd=<tập tin>

Một initrd nên được tải. Ý nghĩa của <file> là rõ ràng là phụ thuộc vào bộ nạp khởi động và một số bộ tải khởi động (ví dụ LILO) không có lệnh như vậy.

Ngoài ra, một số bộ tải khởi động còn thêm các tùy chọn sau vào dòng lệnh do người dùng chỉ định:

BOOT_IMAGE=<tập tin>

Hình ảnh khởi động đã được tải. Một lần nữa, ý nghĩa của <file> rõ ràng là phụ thuộc vào bootloader.

tự động

Kernel đã được khởi động mà không có sự can thiệp rõ ràng của người dùng.

Nếu các tùy chọn này được thêm vào bởi bộ tải khởi động thì sẽ rất nguy hiểm. khuyến nghị rằng chúng nên được đặt ở vị trí ZZ0000ZZ, trước địa chỉ do người dùng chỉ định hoặc dòng lệnh do cấu hình chỉ định. Ngược lại, “init=/bin/sh” bị nhầm lẫn bởi tùy chọn “tự động”.

1.13. Chạy hạt nhân

Hạt nhân được khởi động bằng cách nhảy tới điểm vào của hạt nhân, đó là nằm ở ZZ0000ZZ offset 0x20 kể từ khi bắt đầu chế độ thực hạt nhân. Điều này có nghĩa là nếu bạn đã tải mã hạt nhân chế độ thực của mình tại 0x90000, điểm vào kernel là 9020:0000.

Khi vào, ds = es = ss sẽ trỏ đến điểm bắt đầu của chế độ thực mã hạt nhân (0x9000 nếu mã được tải ở 0x90000), sp phải là được thiết lập đúng cách, thường trỏ đến đỉnh của vùng heap và ngắt nên bị vô hiệu hóa. Hơn nữa, để đề phòng các lỗi trong kernel, bộ tải khởi động nên đặt fs = gs = ds = es = ss.

Trong ví dụ của chúng tôi ở trên, chúng tôi sẽ làm:

/*
  • Lưu ý: trong trường hợp giao thức kernel “cũ”, base_ptr phải

  • be == 0x90000 tại thời điểm này; xem mã mẫu trước đó.

*/

seg = base_ptr >> 4;

cli(); /* Nhập với các ngắt bị vô hiệu hóa! */

/* Thiết lập ngăn xếp kernel ở chế độ thực */

_SS = phân đoạn; _SP = heap_end;

_DS = _ES = _FS = _GS = phân đoạn;

jmp_far(seg + 0x20, 0); /* Chạy hạt nhân */

Nếu khu vực khởi động của bạn truy cập vào ổ đĩa mềm, bạn nên hãy tắt động cơ đĩa mềm trước khi chạy kernel, vì quá trình khởi động kernel bị gián đoạn và do đó động cơ sẽ không hoạt động bị tắt, đặc biệt nếu hạt nhân đã nạp có trình điều khiển đĩa mềm như một mô-đun đáp ứng nhu cầu!

1.14. Móc tải khởi động nâng cao

Nếu bộ tải khởi động chạy trong một môi trường đặc biệt thù địch (chẳng hạn như LOADLIN, chạy dưới DOS), có thể không thể làm theo yêu cầu vị trí bộ nhớ tiêu chuẩn. Bộ tải khởi động như vậy có thể sử dụng các hook sau đây, nếu được thiết lập, sẽ được gọi bởi kernel tại thời điểm thích hợp. Việc sử dụng những chiếc móc này có lẽ nên được coi là phương sách cuối cùng!

IMPORTANT: Cần có tất cả các hook để bảo toàn %esp, %ebp, %esi và %edi qua lời gọi.

realmode_swtch:

Một chương trình con xa ở chế độ thực 16-bit được gọi ngay trước vào chế độ được bảo vệ. Quy trình mặc định sẽ vô hiệu hóa NMI, vì vậy thói quen của bạn có lẽ cũng nên làm như vậy.

mã32_start:

Một quy trình chế độ phẳng 32-bit ZZ0000ZZ ngay sau chuyển sang chế độ được bảo vệ, nhưng trước khi kernel được không nén. Không có phân đoạn nào, ngoại trừ CS, được đảm bảo là thiết lập (hạt nhân hiện tại thì có, nhưng hạt cũ thì không); bạn nên hãy tự thiết lập chúng thành BOOT_DS (0x18).

Sau khi hook xong bạn nên nhảy tới địa chỉ

nó nằm trong trường này trước khi bộ tải khởi động của bạn ghi đè lên nó (di dời, nếu thích hợp.)

1.15. Giao thức khởi động 32-bit

Đối với máy có một số BIOS mới ngoài BIOS cũ, chẳng hạn như EFI, LinuxBIOS, v.v. và kexec, mã thiết lập chế độ thực 16 bit trong kernel không thể sử dụng dựa trên BIOS kế thừa, do đó cần có giao thức khởi động 32 bit được xác định.

Trong giao thức khởi động 32 bit, bước đầu tiên trong quá trình tải nhân Linux nên thiết lập các tham số khởi động (struct boot_params, theo truyền thống được gọi là “trang không”). Bộ nhớ cho struct boot_params nên được phân bổ và khởi tạo bằng 0. Sau đó, tiêu đề thiết lập từ offset 0x01f1 của ảnh kernel trở đi nên được tải vào struct boot_params và kiểm tra. Tiêu đề kết thúc thiết lập có thể được tính như sau theo dõi:

0x0202 + giá trị byte ở offset 0x0201

Ngoài việc đọc/sửa đổi/ghi tiêu đề thiết lập của cấu trúc boot_params giống như giao thức khởi động 16-bit, bộ tải khởi động sẽ cũng điền vào các trường bổ sung của struct boot_params như được mô tả trong chương Zero Page.

Sau khi thiết lập cấu trúc boot_params, bộ tải khởi động có thể tải Kernel 32/64-bit giống như giao thức khởi động 16-bit.

Trong giao thức khởi động 32-bit, kernel được khởi động bằng cách nhảy tới Điểm vào kernel 32-bit, là địa chỉ bắt đầu của quá trình tải Hạt nhân 32/64-bit.

Khi vào, CPU phải ở chế độ được bảo vệ 32 bit với tính năng phân trang bị vô hiệu hóa; GDT phải được tải cùng với bộ mô tả cho bộ chọn __BOOT_CS(0x10) và __BOOT_DS(0x18); cả hai mô tả phải là 4G phẳng phân đoạn; __BOOT_CS phải có quyền thực thi/đọc và __BOOT_DS phải có quyền đọc/ghi; CS phải là __BOOT_CS và DS, ES, SS phải là __BOOT_DS; ngắt phải bị vô hiệu hóa; %esi phải giữ căn cứ địa chỉ của struct boot_params; %ebp, %edi và %ebx phải bằng 0.

1.16. Giao thức khởi động 64-bit

Đối với máy có cpu 64bit và kernel 64bit, chúng ta có thể sử dụng bootloader 64bit và chúng ta cần một giao thức khởi động 64-bit.

Trong giao thức khởi động 64-bit, bước đầu tiên trong quá trình tải nhân Linux nên thiết lập các tham số khởi động (struct boot_params, theo truyền thống được gọi là “trang không”). Bộ nhớ cho struct boot_params có thể được phân bổ ở bất cứ đâu (thậm chí trên 4G) và được khởi tạo bằng 0. Sau đó, tiêu đề thiết lập ở offset 0x01f1 của ảnh kernel sẽ là được tải vào struct boot_params và kiểm tra. Tiêu đề kết thúc thiết lập có thể được tính như sau:

0x0202 + giá trị byte ở offset 0x0201

Ngoài việc đọc/sửa đổi/ghi tiêu đề thiết lập của cấu trúc boot_params giống như giao thức khởi động 16-bit, bộ tải khởi động sẽ cũng điền vào các trường bổ sung của struct boot_params như được mô tả trong chương Zero Page.

Sau khi thiết lập cấu trúc boot_params, bộ tải khởi động có thể tải Hạt nhân 64-bit giống như giao thức khởi động 16-bit, nhưng kernel có thể được tải trên 4G.

Trong giao thức khởi động 64-bit, kernel được khởi động bằng cách nhảy tới Điểm vào kernel 64-bit, là địa chỉ bắt đầu của quá trình tải Hạt nhân 64 bit cộng với 0x200.

Khi vào, CPU phải ở chế độ 64-bit và bật tính năng phân trang. Phạm vi có setup_header.init_size từ địa chỉ bắt đầu được tải kernel và trang zero và bộ đệm dòng lệnh lấy ánh xạ nhận dạng; GDT phải được tải cùng với bộ mô tả cho bộ chọn __BOOT_CS(0x10) và __BOOT_DS(0x18); cả hai mô tả phải là 4G phẳng phân đoạn; __BOOT_CS phải có quyền thực thi/đọc và __BOOT_DS phải có quyền đọc/ghi; CS phải là __BOOT_CS và DS, ES, SS phải là __BOOT_DS; ngắt phải bị vô hiệu hóa; %rsi phải giữ chân đế địa chỉ của struct boot_params.

1.17. Giao thức chuyển giao EFI (không dùng nữa)

Giao thức này cho phép bộ tải khởi động trì hoãn việc khởi tạo EFI cuống khởi động. Cần có bộ tải khởi động để tải kernel/initrd từ phương tiện khởi động và nhảy tới điểm vào giao thức chuyển giao EFI đó là hdr->handover_offset byte từ đầu khởi động_{32,64}.

Bộ tải khởi động MUST tôn trọng siêu dữ liệu PE/COFF của kernel khi nó xuất hiện để căn chỉnh phần, dung lượng bộ nhớ của hình ảnh thực thi vượt quá kích thước của chính tệp đó và bất kỳ khía cạnh nào khác của tiêu đề PE/COFF có thể ảnh hưởng đến hoạt động chính xác của hình ảnh dưới dạng nhị phân PE/COFF trong bối cảnh thực thi được cung cấp bởi phần sụn EFI.

Nguyên mẫu hàm cho điểm vào chuyển giao trông như thế này:

void efi_stub_entry(void *handle, efi_system_table_t *table, struct boot_params *bp);

‘xử lý’ là trình xử lý hình ảnh EFI được EFI chuyển đến bộ tải khởi động firmware, ‘table’ là bảng hệ thống EFI - đây là hai bảng đầu tiên các đối số của “trạng thái chuyển giao” như được mô tả trong phần 2.3 của Thông số kỹ thuật UEFI. ‘bp’ là thông số khởi động được phân bổ bởi bộ tải khởi động.

Bộ tải khởi động ZZ0000ZZ điền vào các trường sau trong bp:

- hdr.cmd_line_ptr
  • hdr.ramdisk_image (nếu có)

  • hdr.ramdisk_size (nếu có)

Tất cả các trường khác phải bằng 0.

Lưu ý

The EFI Handover Protocol is deprecated in favour of the ordinary PE/COFF entry point described below.

1.18. Điểm vào PE/COFF

Khi được biên dịch bằng ZZ0000ZZ, kernel có thể được thực thi dưới dạng nhị phân PE/COFF thông thường. Xem Tài liệu/admin-guide/efi-stub.rst để biết chi tiết thực hiện.

Trình tải sơ khai có thể yêu cầu initrd thông qua giao thức UEFI. Để làm việc này, phần sụn hoặc bộ nạp khởi động cần đăng ký một tay cầm mang triển khai giao thức ZZ0000ZZ và đường dẫn thiết bị giao thức hiển thị đường dẫn thiết bị đa phương tiện của nhà cung cấp ZZ0001ZZ. Trong trường hợp này, kernel khởi động qua sơ khai EFI sẽ gọi Phương pháp ZZ0002ZZ trên giao thức đã đăng ký để hướng dẫn chương trình cơ sở để tải initrd vào vị trí bộ nhớ được chọn bởi kernel/EFI sơ khai.

Cách tiếp cận này loại bỏ sự cần thiết phải có bất kỳ kiến thức nào về phía EFI bộ nạp khởi động liên quan đến biểu diễn bên trong của boot_params hoặc bất kỳ yêu cầu/hạn chế liên quan đến vị trí của dòng lệnh và ramdisk trong bộ nhớ hoặc vị trí của hình ảnh hạt nhân.

Để biết cách triển khai mẫu, hãy tham khảo ZZ0000ZZ hoặc ZZ0001ZZ.