Giới thiệu
Để hiểu rõ cơ chế hoạt động của bộ khởi động (boot sector), chúng ta cần nắm vững quy trình khởi động từ BIOS. Khi máy tính được bật nguồn, BIOS sẽ thực hiện kiểm tra phần cứng, sau đó đọc boot sector (512 byte) từ thiết bị khởi động và nạp nó vào địa chỉ bộ nhớ 0x7C00. Nhiệm vụ chính của đoạn mã này là tìm và tải kernel vào bộ nhớ, sau đó chuyển quyền điều khiển cho hệ điều hành.
Cấu trúc chương trình
Chương trình boot sector được chia thành các phần chính sau:
- Thiết lập môi trường ban đầu (thanh ghi, stack, xóa màn hình)
- Đặt lại đĩa mềm và chuẩn bị thao tác đọc
- Duyệt qua thư mục gốc để tìm file kernel
- Nếu tìm thấy, tải nội dung file theo cấu trúc FAT12
- Nhảy đến điểm nhập của kernel
- Nếu không tìm thấy, hiển thị thông báo lỗi và dừng
Chi tiết từng phần
1. Định nghĩa các hằng số và tham số
org 0x7c00
; Tham số hệ thống file FAT12 cho đĩa mềm 1.44MB
STACK_ADDR equ 0x7c00
KERNEL_SEG equ 0x1000
KERNEL_OFF equ 0x00
ROOT_SECTS equ 14
ROOT_START equ 19
FAT1_START equ 1
SECTOR_OFFSET equ 17
Các tham số trên xác định cấu trúc của đĩa mềm 1.44MB theo chuẩn FAT12, bao gồm vị trí của bảng FAT, thư mục gốc và các thông số khác.
2. Khởi tạo môi trường
init_environment:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, STACK_ADDR
; Xóa màn hình bằng BIOS interrupt 10h/06h
mov ax, 0600h
mov bx, 0700h
mov cx, 0
mov dx, 0184Fh
int 10h
; Đặt con trỏ tại vị trí (0,0)
mov ax, 0200h
mov bx, 0000h
mov dx, 0000h
int 10h
; Hiển thị thông báo khởi động
mov ax, 1301h
mov bx, 000Fh
mov dx, 0000h
mov cx, msg_len
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, boot_msg
int 10h
Đoạn mã này thiết lập các thanh ghi đoạn, khởi tạo stack, xóa màn hình và hiển thị thông báo khởi động cho người dùng.
3. Đặt lại đĩa mềm
reset_disk:
xor ah, ah
xor dl, dl
int 13h
Việc reset đĩa là cần thiết để đảm bảo đĩa ở trạng thái sẵn sàng cho các thao tác đọc tiếp theo.
4. Tìm kiếm file kernel trong thư mục gốc
search_kernel:
mov word [remaining_sectors], ROOT_SECTS
mov word [current_sector], ROOT_START
next_root_sector:
cmp word [remaining_sectors], 0
jz not_found
dec word [remaining_sectors]
; Đọc một sector của thư mục gốc vào địa chỉ 0x8000
mov ax, 00h
mov es, ax
mov bx, 8000h
mov ax, [current_sector]
mov cl, 1
call read_sector
; So sánh tên file
mov si, kernel_name
mov di, 8000h
cld
mov dx, 11
compare_loop:
cmp dx, 0
jz found_kernel
dec dx
mov cx, 11
char_compare:
cmp cx, 0
jz next_entry
dec cx
lodsb
cmp al, byte [es:di]
jz char_equal
jmp next_entry
char_equal:
inc di
jmp char_compare
next_entry:
and di, 0FFE0h
add di, 20h
mov si, kernel_name
jmp compare_loop
found_kernel:
; Xử lý khi tìm thấy file
mov ax, ROOT_SECTS
and di, 0FFE0h
add di, 01Ah
mov cx, word [es:di]
push cx
; Tiếp tục tải file...
jmp load_kernel
not_found:
; Hiển thị thông báo lỗi
mov ax, 1301h
mov bx, 008Ch
mov dx, 0100h
mov cx, err_len
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, error_msg
int 10h
jmp $
Quá trình tìm kiếm duyệt qua từng entry trong thư mục gốc (mỗi entry 32 byte), so sánh từng ký tự của tên file. Thư mục gốc lưu trữ thông tin về tất cả các file bao gồm tên, phần mở rộng, cluster bắt đầu và kích thước.
5. Tải file kernel vào bộ nhớ
load_kernel:
mov ax, KERNEL_SEG
mov es, ax
mov bx, KERNEL_OFF
load_loop:
push ax
push bx
; Hiển thị dấu chấm để báo tiến trình
mov ah, 0Eh
mov al, '.'
mov bl, 0Fh
int 10h
pop bx
pop ax
; Đọc một cluster
mov cl, 1
call read_sector
; Lấy cluster tiếp theo từ bảng FAT
call get_fat_entry
cmp ax, 0FFFh
jz done_loading
push ax
mov dx, ROOT_SECTS
add ax, dx
add ax, SECTOR_OFFSET
add bx, 512
jmp load_loop
done_loading:
jmp KERNEL_SEG:KERNEL_OFF
Cơ chế tải file dựa trên chuỗi các cluster: mỗi cluster chứa một sector, bảng FAT lưu trữ cluster tiếp theo của file. Khi gặp giá trị 0FFFh, có nghĩa là đã đến cuối chuỗi cluster.
6. Hàm đọc một sector (LBA sang CHS)
read_sector:
push bp
mov bp, sp
sub esp, 2
mov byte [bp-2], cl
push bx
; Chuyển đổi LBA sang CHS
mov bl, 18
div bl
inc ah
mov cl, ah
mov dh, al
shr al, 1
mov ch, al
pop bx
mov dl, [drive_number]
retry_read:
mov ah, 2
mov al, byte [bp-2]
int 13h
jc retry_read
add esp, 2
pop bp
ret
BIOS sử dụng địa chỉ CHS (Cylinder-Head-Sector) thay vì LBA (Logical Block Addressing). Hàm này thực hiện phép chuyển đổi dựa trên tham số của đĩa mềm (18 sector/track, 2 head).
7. Hàm lấy giá trị từ bảng FAT
get_fat_entry:
push es
push bx
push ax
mov ax, 00h
mov es, ax
pop ax
mov byte [is_odd], 0
; Tính toán vị trí trong bảng FAT
mov bx, 3
mul bx
mov bx, 2
div bx
mov bx, 8000h
add ax, FAT1_START
mov cl, 1
mul cx
pop dx
add bx, dx
mov ax, [es:bx]
; Xử lý trường hợp cluster chẵn/lẻ
cmp byte [is_odd], 1
jnz even_cluster
shr ax, 4
even_cluster:
and ax, 0FFFh
pop bx
pop es
ret
FAT12 sử dụng 12 bit (1.5 byte) cho mỗi entry. Với cluster chẵn, lấy 12 bit thấp; với cluster lẻ, lấy 12 bit cao bằng cách dịch phải 4 bit.
8. Định nghĩa biến và chuỗi
remaining_sectors dw ROOT_SECTS
current_sector dw 0
is_odd db 0
boot_msg db "Start Boot"
msg_len equ $ - boot_msg
error_msg db "ERROR:No KERNEL Found"
err_len equ $ - error_msg
kernel_name db "KERNEL BIN",0
9. Boot signature
times 510 - ($ - $$) db 0
dw 0xAA55
Hai byte cuối cùng của boot sector phải là 0xAA55 để BIOS nhận diện đây là sector khởi động hợp lệ.
Tổng kết
Việc nắm vững nguyên lý hoạt động của boot sector là nền tảng quan trọng cho việc phát triển hệ điều hành. Dù công nghệ phần cứng ngày càng phát triển (UEFI, NVMe), nhưng các khái niệm cốt lõi như khởi tạo, tìm kiếm file, và chuyển giao quyền điều khiển vẫn giữ nguyên giá trị.
Tài liệu tham khảo:
- FAT12 Filesystem Specification (Microsoft)
- BIOS Interrupt Reference
- Assembly Language Programming