1 คะแนน โดย GN⁺ 2024-07-16 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • เริ่มจาก boot sector ขนาด 512 ไบต์ แล้วจัดลำดับขั้นตอนของ bootloader ขั้นต่ำที่ยก CPU x86_64 จาก real mode 16 บิตขึ้นสู่ long mode 64 บิต
  • ตรวจสอบการแสดงผลด้วย BIOS interrupt ก่อน จากนั้นเชื่อม nasm, ld, objcopy และ QEMU เพื่อตรวจว่าภาพ boot image รันได้จริงหรือไม่
  • เนื่องจากข้อจำกัดด้านขนาดของ boot sector จึงแบ่งเป็น stage 1/stage 2 และก่อนเข้าสู่ protected mode จะอ่านโค้ดถัดไปจากดิสก์ด้วย BIOS int 0x13
  • ตั้งแต่ protected mode 32 บิตเป็นต้นไป จะใช้ BIOS routine ไม่ได้ จึงต้องเริ่มต้นฮาร์ดแวร์เอง เช่น GDT, flat segmentation และการเขียนออก VGA buffer โดยตรง
  • การเข้าสู่ long mode 64 บิตต้องตั้งค่า page table, PAE, EFER.LME, cr0.PG และ GDT สำหรับ 64 บิตให้ถูกต้องทั้งหมด หลังจากนั้นจึงสามารถเรียก โค้ด C แบบ freestanding ได้เหมือนเคอร์เนล

สภาพแวดล้อม 16 บิตที่เริ่มจาก BIOS

  • หลัง reset แล้ว CPU x86 จะอยู่ใน real mode และขนาด operand พื้นฐานคือ 16 บิต
  • real mode ใช้ segmentation เพื่อสร้างพื้นที่ที่อยู่ 20 บิต และจัดการหน่วยความจำได้สูงสุด 1MB
  • โค้ดแรกที่ BIOS รันอยู่ใน boot sector ของดิสก์
    • BIOS จะค้นหาดิสก์ที่ sector แรกจบด้วย magic number 0xaa55
    • โหลด sector นั้นไปยังที่อยู่หน่วยความจำ 0x7c00
  • พื้นที่ที่ BIOS ส่งต่อให้มีเพียง 512 ไบต์ ดังนั้นโค้ดนี้จึงมุ่งทำหน้าที่เป็น bootstrap เพื่อโหลด bootloader ส่วนที่เหลือ
  • BIOS routine ใช้ได้เฉพาะขณะที่ยังอยู่ใน real mode เท่านั้น

สิ่งที่ต้องมีและสภาพแวดล้อมสำหรับ build

  • หากต้องการทำตาม จำเป็นต้องมี Intel 64 and IA-32 Architectures Software Developer’s Manual, assembler และ QEMU
  • assembler ในตัวอย่างคือ nasm โดยดำเนินเรื่องบนสมมติฐานว่าผู้อ่านรู้จัก x86 assembly และไวยากรณ์ของ nasm แล้ว
  • แม้ไม่มี CPU x86_64 ก็สามารถรันได้โดย emulate CPU x86 ใน QEMU
  • ใช้ Writing a Simple Operating System — from Scratch เป็นเอกสารพื้นฐานจนถึงโหมด 32 บิต

สร้าง boot sector และตรวจสอบการรัน

  • boot sector แรกใช้ BIOS routine แสดง "Hello, world!" แล้วหยุดด้วย hlt และ loop
  • การแสดงสตริงใช้ int 0x10 และ ah = 0x0e ซึ่งเป็นการเรียก BIOS video services
  • Makefile สร้าง object ด้วย nasm, link ด้วย linker script แล้วสร้าง raw boot image ด้วย objcopy -O binary
  • make boot รัน image ด้วย QEMU
    • qemu-system-x86_64 -no-reboot -drive file=$<,format=raw,index=0,media=disk
  • linker script ตั้งค่าให้ boot sector ถูกวางโดยอิงจาก 0x7c00
    • origin ของ boot_sector ใน MEMORY คือ 0x7c00 และ length คือ 512
    • section .bootsign เพิ่ม 0x55, 0xaa ที่ตำแหน่ง 0x7c00 + 510
  • แม้จะจัดการ offset และ magic number โดยตรงใน assembly ของ boot sector ได้ แต่ที่นี่ให้ linker script รับหน้าที่นั้น

ให้ stage 1 อ่าน stage 2 จากดิสก์

  • stage 1 คือ โค้ด boot sector ที่ BIOS โหลด และเป้าหมายคือโหลด stage 2 เข้าสู่หน่วยความจำ
  • stage 2 มีโค้ดสำหรับย้ายจาก real mode 16 บิตไปยัง protected mode 32 บิต
  • หลังเข้าสู่ protected mode แล้วจะใช้ BIOS routine ไม่ได้ ดังนั้นการอ่าน sector จากดิสก์ต้องเสร็จก่อนการสลับโหมด
  • การเข้าถึงดิสก์ใช้ BIOS disk services int 0x13
    • ah = 0x42 คือฟังก์ชัน BIOS extended read
    • dl = 0x80 คือหมายเลขไดรฟ์
    • disk address packet มีจำนวน sector ที่จะอ่าน, ที่อยู่ปลายทาง และ sector เริ่มต้น
  • ตัวอย่างอ่าน 64 sector ด้วย READ_SECTORS_NUM equ 64
    • boot sector คือ sector 0 ดังนั้น stage 2 จะอ่านตั้งแต่ sector 1
    • ที่อยู่ปลายทางคือ BOOT_LOAD_ADDR + SECTOR_SIZE หรือ 0x7c00 + 512
  • ในโค้ดยังมีการจัดการชั่วคราวที่ยอมรับกรณีอ่าน sector ได้น้อยกว่าที่ร้องขอ
  • ตอนแรก stage 2 คัดลอก print_string สำหรับ real mode เพื่อแสดง "Hello from stage 2" และตรวจว่ายัง jump ต่อจาก stage 1 ได้ถูกต้องหรือไม่

สลับไปยัง protected mode 32 บิต

  • หากจะเข้าสู่ protected mode ต้องนิยาม Global Descriptor Table(GDT) ก่อน
  • ใน protected mode โดยพื้นฐานแล้ว segmentation ใช้สำหรับการป้องกันหน่วยความจำ
  • ใน long mode 64 บิตจำเป็นต้องใช้ paging แต่ก่อนถึงขั้นนั้น การเข้าสู่ protected mode ต้องตั้งค่า segmentation ก่อน
  • GDT ในตัวอย่างทำตาม flat model จาก Intel manual
    • มี code segment และ data segment
    • ทั้งสอง segment map ไปยัง linear address space ทั้งหมด
    • ใช้โมเดลที่ง่ายที่สุดเพราะเป็นขั้นกลางเพื่อไปยัง long mode
  • GDT เป็นโครงสร้างต่อเนื่องที่วางอยู่ในหน่วยความจำ
    • จุดเริ่มต้นมี null descriptor สำหรับดัก invalid translation
    • ถัดมาคือ code segment descriptor และ data segment descriptor
  • การสลับโหมดทำตามลำดับต่อไปนี้
    • ปิดใช้งาน interrupt ด้วย cli
    • โหลดที่อยู่และความยาวของ GDT เข้า GDTR ด้วย lgdt [gdt32_pseudo_descriptor]
    • เปิด protected mode โดยตั้งค่า cr0.PE หรือ bit 0 ของ cr0
    • ล้าง instruction pipeline และอัปเดต cs เป็น code segment ใหม่ด้วย far jump
  • หลังเข้าสู่ protected mode แล้ว ค่า segment เดิมไม่มีความหมายอีกต่อไป จึงตั้ง ds, ss, es, fs, gs เป็น data segment selector ใหม่
  • หากต้องการเปิด interrupt อีกครั้งหลังตั้งค่าทั้งหมดแล้ว จำเป็นต้องมีงานเพิ่มเติม

แสดงผลบนหน้าจอโดยไม่ใช้ BIOS

  • ใน protected mode จะเรียก BIOS routine ไม่ได้อีกต่อไป
  • การแสดงสตริงเปลี่ยนเป็นการเขียนลง VGA buffer โดยตรง
  • print_string32 บันทึกตัวอักษรและ byte สีลงที่อยู่ 0xb8000
    • ค่าสีคือ 0xf
    • cell ของตัวอักษรแต่ละตัวใช้ 2 ไบต์
  • ฟังก์ชันแสดงผลนี้เรียบง่ายมาก ข้อความจึงแสดงที่มุมซ้ายบนของหน้าจอเสมอ

page table สำหรับเข้าสู่ long mode

  • IA-32e mode ในเอกสารของ Intel สอดคล้องกับ long mode ใน AMD64 manual
  • หากต้องการสลับไป long mode CPU ต้องอยู่ใน protected mode และต้องเปิดใช้ paging ด้วย
  • แนวคิดเรื่อง paging อ้างอิงจาก Introduction to Paging และ OSTEP
  • ใน long mode ที่เปิดใช้ PAE จะใช้ page table 4 ระดับ
  • build_page_table สร้าง page table 4 ระดับที่ที่อยู่ที่กำหนด
    • ขนาด page คือ 0x1000
    • ขนาดของ page table แต่ละตัวคือ 0x1000
    • จำนวน entry คือ 512
    • เริ่มจากเคลียร์ table สี่ตัวเป็น 0 เพื่อให้ entry ทั้งหมดอยู่ในสถานะ not present
    • เชื่อม entry แรก ๆ ของ PML4 → PDP → PD → page table
    • ตั้งค่า entry 512 รายการใน layer page table ต่ำสุด

GDT สำหรับ 64 บิตและลำดับการสลับเข้า long mode

  • แม้ paging จะดูแล virtual address space และการจัดการสิทธิ์ แต่ใน long mode ก็ยังต้องมี GDT
  • GDT สำหรับ 64 บิตก็ทำตาม flat model และแทบเหมือนกับ GDT สำหรับ protected mode
  • ความแตกต่างอยู่ที่การตั้งค่า bit ที่เกี่ยวข้องกับ long mode
    • ตั้งค่า flag 64-bit code segment ของ code segment
    • เมื่อ flag นี้ถูกตั้ง bit default operation size ต้องเป็น 0
  • การสลับเข้า long mode ดำเนินตาม flow ต่อไปนี้
    • สร้าง page table 4 ระดับที่ที่อยู่ 0x1000
    • ใส่ที่อยู่ PML4 table ลงใน cr3
    • เปิดใช้ PAE โดยตั้งค่า bit 5 ของ cr4
    • อ่าน MSR 0xc0000080 และตั้งค่า EFER.LME หรือ bit 8
    • เปิดใช้ paging โดยตั้งค่า flag PG ซึ่งเป็น bit 31 ของ cr0
    • โหลด GDT สำหรับ 64 บิตด้วย lgdt
    • เข้าโหมด 64 บิตโดย far jump ไปยัง code segment แบบ 64 บิต
  • ทันทีหลังเปิด paging จะอยู่ในสถานะ IA-32e compatibility mode และจะสลับเป็นโหมด 64 บิตเมื่อ jump ไปยัง GDT ที่ตั้งค่า flag segment 64 บิตไว้
  • ข้อความยืนยันความสำเร็จแสดงที่มุมซ้ายบนของหน้าจอผ่าน VGA buffer

เรียกโค้ด C แบบ freestanding

  • เมื่อเข้าสู่ long mode 64 บิตแล้ว จะสามารถเรียก โค้ด C แบบ freestanding ได้
  • kernel.c เคลียร์ VGA buffer 0xb8000 แล้วแสดง "Hello from C"
  • start_long_mode ฝั่ง assembly หลังจากแสดงสตริงสำหรับ 64 บิตแล้ว จะประกาศ extern _start_kernel และเรียก _start_kernel
  • linker script แบ่งพื้นที่หน่วยความจำออกเป็นสามส่วน
    • boot_sector: 0x7c00, length 512
    • stage2: 0x7e00, length 512
    • kernel: 0x8000, length 0x10000
  • section .text, .data, .rodata, .bss ถูกวางในพื้นที่ kernel
  • Makefile ถูกเปลี่ยนให้ build ทั้ง assembly และ C
    • C compiler คือ gcc
    • CFLAGS หลักคือ -std=c99 -ffreestanding -m64 -mno-red-zone -fno-builtin -nostdinc -Wall -Wextra
  • โค้ดตัวอย่างทั้งหมดมีให้ที่ ลิงก์ดาวน์โหลด

1 ความคิดเห็น

 
GN⁺ 2024-07-16
ความคิดเห็นจาก Hacker News
  • สามารถเข้าสู่ long mode ได้โดยตรงด้วยโค้ดที่น้อยกว่ามาก โดยไม่ต้องผ่าน protected mode: https://wiki.osdev.org/Entering_Long_Mode_Directly
    เคยมี bootloader สำหรับเคอร์เนล 64 บิตขนาดเล็กที่ทำด้วยวิธีนี้ และแม้จะรวมโค้ดสำหรับอ่านเคอร์เนลจากดิสก์และตั้งค่าโหมด VESA แล้ว ก็ยังใส่ใน boot sector ได้เหลือ ๆ ไม่ต้องมี loader ขั้นที่ 2 ด้วย

    • อยากรู้เหมือนกันว่าเขายัดทั้งหมดนั้นลงใน 512 ไบต์ ได้อย่างไร คงไม่มี file system จริง ๆ ที่วางเคอร์เนลไว้ที่ไหนก็ได้บนดิสก์เหมือนไฟล์ทั่วไป แค่จัดการ file fragmentation ก็น่าจะเกิน 512 ไบต์ไปไกลแล้ว
    • แค่ใช้ https://limine-bootloader.org/ ก็เรียบง่ายกว่ามาก ไม่ต้องยุ่งกับ real mode และกับ SMP ก็เช่นกัน อีกทั้งยังโหลดเคอร์เนลเป็น higher-half mapping ให้โดยอัตโนมัติ และทำงานได้บน aarch64 กับ riscv64 ด้วย
    • พูดถูก แต่ถ้าจะใส่ partition table และรองรับ AHCI controller กับ SATA สมัยใหม่ พื้นที่ bootloader จะยิ่งลดลงจนต้อง optimize ในกรณีนี้ใช้ครบ 510 ไบต์ให้ loader ไม่ได้ ต้องใช้น้อยกว่านั้นมาก และถ้าต้องใส่ partition entry ที่ถูกต้องด้วย ก็ใช้ไบต์ภายในตารางไม่ได้ ทำให้ยากขึ้นอีก
      ถ้าจะใช้ฮาร์ดดิสก์สมัยใหม่จริง ๆ ควรดู GPT มากกว่า MBR เพราะจัดการดิสก์ขนาดใหญ่กว่า 2TB ได้โดยไม่ติดข้อจำกัดของ partition table ส่วน UEFI ทำให้ปัญหาเหล่านี้หมดไป และช่วยให้ใช้ disk layout ที่เหมาะสมได้โดยไม่ยุ่งยากมากนัก
      การเข้าสู่โหมด 64 บิตไม่จำเป็นต้องใช้ protected mode อย่างไรก็ตาม ไม่ควรใช้ BIOS มันรกและมีแต่ทำให้งานยุ่งยากขึ้น
      ใช้ UEFI ผ่าน EDK2 หรือ GnuEFI จะดีกว่า ทั้งสองอย่าง implement ได้ค่อนข้างง่ายและสะดวก แม้ต้องใช้เวลาสักหน่อยกว่าจะคุ้นกับแนวคิดพื้นฐานของ UEFI แต่ดูโปรเจกต์ตัวอย่างใน GitHub ก็จะเข้าใจโครงสร้างได้ง่าย EDK มีไฟล์ .dec กับ .inf ฯลฯ ที่ไม่ค่อยดีนัก ส่วน GnuEFI ต้องอ่าน header file เพื่อหาฟังก์ชัน แต่ก็ยังดีกว่าอินเทอร์เฟซ BIOS ที่สเปกไม่ชัดเจนมาก บนฮาร์ดแวร์จริงแม้แต่ int 0x10, int 0x15 ฯลฯ ก็ยังสรุปไม่ได้ว่าจะมีอย่างถูกต้อง
      บนระบบ UEFI สามารถสมมติฐานขั้นต่ำที่เสถียรได้ และ enumerate ฮาร์ดแวร์หรือฟีเจอร์ของแพลตฟอร์มได้อย่าง sane อีกทั้ง UEFI ตั้งค่าแพลตฟอร์มไว้ค่อนข้างมากแล้ว จึงไม่ต้องทำ initialization มากในคอมโพเนนต์ OS loader แค่โหลด OS, driver และคอมโพเนนต์ให้ตรงกับการออกแบบเคอร์เนลก็พอ รับ memory map แล้วเข้าถึง EFI file system เพื่ออ่านสิ่งที่ต้องใช้เข้ามา
    • ไม่รู้มาก่อนเลยว่าทำแบบนี้ได้ ถ้าเป้าหมายคือแค่ไป long mode ก็สงสัยว่าตั้งแต่แรกทำไมต้องผ่าน protected mode ด้วย
  • 80286 มี Machine Status Word(MSW) ซึ่งเป็นรีจิสเตอร์ 16 บิต และ 80386 ขยายมันเป็นรีจิสเตอร์ 32 บิตชื่อ CR0 ต่อมา long mode แบบ 64 บิตเพิ่ม EFER MSR และขยาย CR0 เป็น 64 บิต แต่แม้ทุกวันนี้ CR0 ก็ใช้แค่ 11 บิต และ EFER ก็มี active bit แค่ 8 บิตเท่านั้น
    สงสัยว่าทำไม Intel/AMD ถึงเลือกทางใหม่ถึงสองครั้ง แทนที่จะใช้บิตที่เหลืออยู่ของรีจิสเตอร์เดิม: https://wiki.osdev.org/CPU_Registers_x86-64#CR0

    • น่าจะเป็นเพราะต้องการให้ backward compatibility แข็งแรงขึ้น มีความเป็นไปได้ที่ซอฟต์แวร์จะสมมติค่าของ reserved bit หรือเขียนค่าไปที่นั่น การจัดสรรบิตในรีจิสเตอร์ฮาร์ดแวร์แบบนี้ค่อนข้าง arbitrary และการใช้บิตสูง ๆ ก็ไม่ได้มีต้นทุนอะไรเป็นพิเศษ
    • ถ้าตอบด้วยคำเดียว ก็คงเป็น bureaucracy องค์กรขนาดใหญ่โดยรวมมักตัดสินใจได้ไม่ดีนักเป็นพิเศษ และมีตัวเลือกที่ไม่สมเหตุสมผลออกมาเยอะ
      เหตุผลที่ CR1 และ CR5~CR7 ยังเป็น reserved แต่กลับมี CR8 ก็น่าจะคล้ายกัน
  • สิ่งที่ดูซับซ้อนเกินจำเป็นที่สุดในบทความนี้คือ Makefile กับ linker script NASM รองรับการ output เป็น flat binary อยู่แล้ว แต่ดูเหมือนผู้เขียนจะมองว่าการใช้แบบนั้น “hacky” เกินไป

    • ส่วนตัวคิดว่า linker script อ่านง่ายและ reasoning ได้ง่ายกว่า flat NASM มาก โดยเฉพาะเมื่อมี source file หลายไฟล์
    • ถูกต้องเต็ม ๆ ภายหลัง Makefile กับ linker script จะกลายเป็นเรื่องปวดหัวสำคัญ แต่ถ้าจะสร้าง flat binary ก็แค่สร้าง flat binary ไปเลย ไม่จำเป็นต้องทำให้มันพองขึ้น
      เมื่อก่อน OS ของผมมีไฟล์ชื่อ make.sh ไว้ล้อเรื่องนี้ ตอนนี้ผมใช้ของหรู ๆ อย่าง ‘file format’ แล้ว ดังนั้น -fbin กับ --oformat=binary จึงกลายเป็นความทรงจำอันเลือนหายไป ผมเคยพยายามอยู่นานที่จะแยกไฟล์ C สำหรับข้อมูลกับไฟล์ C สำหรับโค้ด แล้ว dump เป็น binary ก่อนจะประกอบสัตว์ประหลาดจากตรงนั้น แต่การ link และ load กลายเป็นเรื่องยากเกินไป ใช้ ELF หรือ PE ไปเลยดีกว่า และดูเหมือน format เหล่านั้นก็ทำหน้าที่แบบนั้นจริง ๆ
  • ดูเท่และเหมือนเป็นแบบฝึกหัดที่ดี แต่ไม่รู้ว่า มีประโยชน์ ไหม สงสัยว่ามี UX แบบของเล่น Fisher-Price ที่ใช้ตรวจสอบหรือเปลี่ยน settings ระหว่างรันได้หรือเปล่า
    การ boot คือกระบวนการจากโหมดมินิมี, single-user mode, recovery mode ไปสู่สถานะพร้อมทะยานขึ้น
    ผมใช้ Unix ควบคู่กับผลิตภัณฑ์ Microsoft มาตั้งแต่ยุค Xenix/DOS น่าจะราว ๆ 40 ปีแล้ว สงสัยเหมือนกันว่าช่วงนั้นมันพัฒนาขึ้นแค่ไหน
    Linux ก็ใช้มาตั้งแต่เวอร์ชันสวีเดน หรือก็คือ release แรก และ GNU 0.1 ก็เคยลองใช้
    ขออภัยที่เรียก Xenix ว่า Unix จริง ๆ Xenix เป็นผลิตภัณฑ์เลียนแบบเละ ๆ ที่อยากกลายเป็นของตกยุคตั้งแต่ไม่นานหลังออกวางจำหน่ายจนถึงตอนเสื่อมความนิยม
    Microsoft เหมือนไม่ได้ออกผลิตภัณฑ์ แต่เป็นบริษัทที่เทกระบะทรายแมวใส่ลูกค้า ตัวอย่างล่าสุดคือ Copilot กับ 22H2
    ถ้าดูว่า F1, ดินสอ, เครื่องคิดเลขพกพาพัฒนาไปอย่างไร ก็อดสงสัยไม่ได้ว่าเราเข้าใกล้อุดมคติที่ใช้งานได้จริงแค่ไหนแล้ว
    ยังสงสัยด้วยว่าทำไม bootloader ถึงไม่เป็น static kernel mode เมื่อก่อนมันเคยเป็นแบบนั้น และเมื่อเร็ว ๆ นี้มีคนเสนอว่าควรกลับไปเป็นแบบนั้นอีก ผมก็เห็นด้วย

  • https://wiki.osdev.org/A20_Line

  • รู้สึกประหลาดใจที่ทุกขั้นตอนที่จำเป็นสำหรับการสลับ CPU ไปยังโหมดที่ถูกต้องดูเหมือนไม่จำเป็นทั้งหมด ส่วนใหญ่ดูเป็นขั้นตอนที่ต้องมีเพราะ ความเข้ากันได้ย้อนหลัง
    สงสัยว่า Intel ไม่สามารถให้แฟล็กหรือคำสั่งสำหรับเริ่มต้นในโหมดที่ถูกต้องตั้งแต่แรกได้หรือไม่ หรือไม่สามารถตัดความเข้ากันได้ย้อนหลังออกไปได้หรือเปล่า
    จำได้ว่า ARM64 ก็มีปัญหาคล้าย ๆ กันอยู่บ้างเหมือนกัน สงสัยว่ามี CPU ที่ออกแบบมาเป็น 64 บิตตั้งแต่แรก จึงไม่ต้องการความเข้ากันได้ย้อนหลัง และโดยพื้นฐานแล้วเข้าสู่สถานะที่ต้องการได้เลยหรือไม่ หรือเป้าหมาย/การออกแบบของ Itanium เป็นแบบนั้นหรือเปล่า

    • X86S ที่ Intel เสนอมีจุดประสงค์แบบนั้น

      X86S is a legacy-reduced-OS ISA that removes outdated execution modes and operating system ISA.
      The presence of the X86S ISA is enumerated by a single, main CPUID feature LEGACY_REDUCED_ISA in CPUID 7.1.ECX[2] which implies all the ISA removals described in this document. A new, 64-bit “start-up” interprocessor interrupt (SIPI) has a separate CPUID feature flag.
      [0] https://cdrdv2.intel.com/v1/dl/getContent/776648 [ระวัง PDF]

    • Intel เคยลองทำแบบนั้นด้วย 80376 แต่ไม่ประสบความสำเร็จ: https://en.wikipedia.org/wiki/Intel_80376
      Itanium หรือที่เรียกว่า Itanic ก็เช่นกัน
      ความเข้ากันได้ย้อนหลังนี่แหละคือเหตุผลหลักที่เลือก x86 แทน ARM, MIPS, RISC-V ฯลฯ น่าเสียดายที่บางคนใน Intel และ AMD ดูเหมือนจะไม่ค่อยเข้าใจเรื่องนี้
    • มี UEFI อยู่แล้ว ถ้าใส่ไบนารีที่คล้าย Windows ไว้ในโฟลเดอร์ของพาร์ทิชัน มันจะถูกรันในสภาพแวดล้อมโฮสต์แบบโหมด 64 บิต แน่นอนว่ายังมีบูตโหลดเดอร์อีกมากมายที่จัดการเรื่องแบบนี้แทนให้
    • ไม่รู้ว่า arm64 มีปัญหาอะไร
  • เป็นโปรเจกต์ที่ยอดเยี่ยม ถ้าผู้สนับสนุน UEFI ตรงนี้สงสัยว่าทำไมต้องสร้างวิธีบูตโหลดเดอร์แบบใหม่ขึ้นมา ผมคิดว่าพวกเขาพลาดเหตุผลที่ผู้คนทำงานแบบนี้ไป
    อย่างที่ผู้เขียนเขียนไว้ตอนท้ายว่า “ถ้าคุณตามมาถึงตรงนี้ได้ ก็เจ๋งมาก” ซึ่งก็เจ๋งจริง ๆ

  • สงสัยว่า UEFI ออกมานานแค่ไหนแล้ว น่าเสียดายที่น่าจะเลิกใช้ BIOS ไปพร้อมกับ long mode เสียเลย

    • BIOS ถูกกำหนดให้ เลิกใช้ อยู่แล้ว ในเมนบอร์ดรุ่นใหม่ ฟังก์ชันนั้นโดยพื้นฐานถูกจำลองผ่าน UEFI และไม่ได้มีการขยายความสามารถเพิ่ม
      คำว่าเลิกใช้ไม่ได้หมายความว่าถูกลบออกแล้ว แต่หมายความว่าไม่อัปเดตหรือพัฒนาต่ออีก โดยมีเป้าหมายเพื่อเอาออก
  • สงสัยว่าขั้นตอนการบูตนี้ทำงานบน EFI/UEFI ด้วยหรือไม่ ถ้าทำงานได้ ก็สงสัยว่า UEFI supervisor จำลองการสลับระหว่าง real mode, protected mode, long mode หรือว่าทำบนฮาร์ดแวร์จริง

    • ไม่ใช่ เฟิร์มแวร์ UEFI ให้สภาพแวดล้อมกับ UEFI bootloader ที่แตกต่างจากสภาพแวดล้อม BIOS แบบเลกาซี หรือก็คือ real-address mode อย่างสิ้นเชิง เฟิร์มแวร์ UEFI ในระบบสมัยใหม่จะเข้าสู่ 64-bit long mode ทันที และตั้งค่า GDT แบบ flat memory model กับ paging แบบ identity mapping ไว้ด้วย
      ผมเขียนขั้นตอนการทำ UEFI bootloader สำหรับ OS งานอดิเรกไว้ที่นี่: https://0xc0ffee.netlify.app/osdev/05-bootloader-p1.html
  • สงสัยว่าบน ARM เรื่องนี้ง่ายกว่าหรือไม่

    • ง่ายในแง่ที่ว่าผู้ผลิตบอร์ดแต่ละรายมีส่วนที่ทำตามใจตัวเอง สำหรับผู้ผลิตบอร์ดมันง่าย แต่สำหรับคนอื่นทั้งหมด มันซับซ้อนอย่างน่ากลัว
    • ใช่ บูตโหลดเดอร์ยังคงซับซ้อนอยู่ แต่ การตั้งค่าเลกาซี ที่จำเป็นมีน้อยกว่า อย่างไรก็ตาม ถ้าตั้งเป้าไปที่ UEFI แทน BIOS บน x86 ก็จะง่ายขึ้นมากเช่นกัน
    • ไม่แน่ใจ และก็ไม่ได้คาดหวัง ตอนนี้กำลังขุดลึกกับ RISC-V อยู่ ซึ่งตรงนั้นดูเหมือนจะมีความหวัง