Bài viết

Bootloader: Viết Bootloader đầu tiên cho kiến trúc x86

Viết bootloader đầu tiên cho kiến trúc x86 với assembly NASM, hiểu quá trình boot từ BIOS và test trên QEMU.

Bootloader: Viết Bootloader đầu tiên cho kiến trúc x86

1. Quá trình khởi động máy tính

1.1. Power Supply Unit (PSU)

Máy tính của chúng ta khởi động như thế nào? Chắc hẳn nhiều bạn từng tự hỏi điều gì thực sự xảy ra khi ta nhấn nút nguồn.

Có một thành phần gọi là PSU (Power Supply Unit) trong phần cứng của hệ thống. Khi bạn nhấn nút nguồn:

  1. PSU nhận tín hiệu từ nút nguồn
  2. Chuyển đổi dòng điện xoay chiều (AC) thành dòng điện một chiều (DC)
  3. Cung cấp năng lượng cho các bộ phận khác của hệ thống
  4. Gửi tín hiệu ‘power-good’ đến BIOS khi điện áp ổn định

1.2. Power On Self Test (POST)

Khi nhận được tín hiệu ‘power-good’, BIOS bắt đầu quá trình POST (Power On Self Test):

  • Kiểm tra nguồn điện có đúng hay không
  • Kiểm tra bộ nhớ có bị hỏng không
  • Phát hiện các thiết bị đang được kết nối
  • Báo lỗi bằng mã số trên I/O port hoặc chuỗi tiếng bíp nếu có vấn đề

1.3. BIOS và Interrupt Vector Table

Sau khi POST hoàn tất, quyền điều khiển được chuyển đến BIOS:

  1. BIOS tạo ra Interrupt Vector Table (IVT)
  2. IVT lưu trữ địa chỉ của các Interrupt Service Routines
  3. Interrupt là cơ chế phần cứng sử dụng để báo hiệu sự kiện cho CPU

1.4. Boot Sector

Công việc chính của BIOS là tải bootloader. Nhưng làm sao biết nó nằm ở đâu?

Boot sector là khu vực đặc biệt trên thiết bị lưu trữ:

  • Thường là sector đầu tiên của disk (sector 0, track 0, head 0)
  • BIOS load dữ liệu từ boot sector vào bộ nhớ tại địa chỉ 0x7c00
  • Sử dụng ngắt 0x19 để nhảy đến địa chỉ này

2. Chuẩn bị môi trường

Chúng ta cần cài đặt các công cụ cần thiết:

1
sudo apt-get install nasm qemu-system-x86
  • NASM: Assembler cho Intel x86 architecture
  • QEMU: Trình giả lập để test bootloader

3. Viết Bootloader đầu tiên

3.1. Source code

Tạo file myboot.asm:

    org 0x7c00
    bits 16

Start: 
    cli
    hlt 

    times 510 - ($-$$) db 0
    dw 0xAA55

3.2. Giải thích từng dòng code

org 0x7c00

  • Bootloader được load vào bộ nhớ tại địa chỉ 0x7c00
  • Yêu cầu NASM set tất cả addresses theo địa chỉ này

bits 16

  • Kiến trúc x86 khởi động ở 16-bit real mode
  • Có thể chuyển sang 32-bit protected mode sau này

clihlt

  • cli: Clear all interrupts
  • hlt: Halt the system
  • Đảm bảo CPU không chạy random instructions

times 510 - ($-$$) db 0

  • Một sector chỉ chứa 512 bytes
  • $: địa chỉ dòng hiện tại
  • $$: địa chỉ dòng đầu tiên
  • ($-$$): kích thước chương trình
  • Padding các byte còn lại bằng 0 để đủ 510 bytes

dw 0xAA55

  • Hai byte cuối (511-512) lưu boot signature
  • 0xAA tại byte 511, 0x55 tại byte 512
  • Cho BIOS biết sector này có thể boot được

4. Build và test Bootloader

4.1. Assemble code

Chuyển đổi assembly code sang machine code:

1
nasm -f bin -o myboot.bin myboot.asm

4.2. Tạo floppy image

Sao chép bootloader vào sector đầu tiên của floppy image:

1
dd status=noxfer conv=notrunc if=myboot.bin of=myboot.flp

4.3. Chạy trên QEMU

Khởi động bootloader đầu tiên của bạn:

1
qemu-system-i386 -fda myboot.flp

QEMU Boot Screen Bootloader chạy thành công trên QEMU

Chúc mừng! Bạn đã viết thành công bootloader đầu tiên cho kiến trúc x86. Mặc dù nó chỉ halt hệ thống, nhưng đây là bước đầu quan trọng để hiểu về low-level system programming.

Bài viết này được cấp phép bởi tác giả theo giấy phép CC BY 4.0 .