ตัวอย่างที่เรียกใช้
ในทางเทคนิคแล้วโปรแกรมที่ทำงานโดยไม่มี OS เป็นระบบปฏิบัติการ ดังนั้นเรามาดูวิธีการสร้างและรันระบบปฏิบัติการ Hello world ขนาดจิ๋ว
โค้ดของตัวอย่างทั้งหมดด้านล่างมีอยู่ในrepo ของ GitHubนี้
บูตเซกเตอร์
บน x86 สิ่งที่ง่ายที่สุดและต่ำที่สุดที่คุณสามารถทำได้คือการสร้างMaster Boot Sector (MBR)ซึ่งเป็นประเภทของบูตเซกเตอร์แล้วติดตั้งลงในดิสก์
ที่นี่เราสร้างขึ้นมาด้วยการprintf
โทรเพียงครั้งเดียว:
printf '\364%509s\125\252' > main.img
sudo apt-get install qemu-system-x86
qemu-system-x86_64 -hda main.img
ผล:
ทดสอบบน Ubuntu 18.04, QEMU 2.11.1
main.img
มีดังต่อไปนี้:
\364
ใน octal == 0xf4
ใน hex: การเข้ารหัสสำหรับhlt
คำสั่งซึ่งบอกให้ CPU หยุดทำงาน
ดังนั้นโปรแกรมของเราจะไม่ทำอะไรเลยเพียง แต่เริ่มและหยุดเท่านั้น
เราใช้ฐานแปดเพราะเลขฐาน\x
สิบหกไม่ได้ถูกระบุโดย POSIX
เราสามารถรับการเข้ารหัสนี้ได้อย่างง่ายดายด้วย:
echo hlt > a.asm
nasm -f bin a.asm
hd a
แต่การ0xf4
เข้ารหัสนั้นมีการบันทึกไว้ในคู่มือ Intel แน่นอน
%509s
ผลิต 509 ช่องว่าง จำเป็นต้องกรอกข้อมูลในไฟล์จนถึงไบต์ 510
\125\252
ใน octal == 0x55
ตามด้วย0xaa
: magic bytes ต้องการโดยฮาร์ดแวร์ พวกเขาจะต้องเป็นไบต์ 511 และ 512
หากไม่มีอยู่ฮาร์ดแวร์จะไม่ถือเป็นดิสก์ที่ใช้บู๊ตได้
โปรดทราบว่าแม้จะไม่ได้ทำอะไรเลยตัวละครบางตัวก็พิมพ์อยู่บนหน้าจอแล้ว เหล่านั้นถูกพิมพ์โดยเฟิร์มแวร์และให้บริการเพื่อระบุระบบ
ทำงานบนฮาร์ดแวร์จริง
อีมูเลเตอร์เป็นเรื่องสนุก แต่ฮาร์ดแวร์เป็นเรื่องจริง
โปรดทราบว่านี่เป็นอันตรายและคุณสามารถล้างดิสก์ของคุณโดยไม่ได้ตั้งใจ: ทำสิ่งนี้กับเครื่องเก่าที่ไม่มีข้อมูลที่สำคัญ! หรือดียิ่งกว่า devboards เช่น Raspberry Pi ดูตัวอย่าง ARM ด้านล่าง
สำหรับแล็ปท็อปทั่วไปคุณต้องทำสิ่งต่อไปนี้:
เบิร์นภาพเป็นแท่ง USB (จะทำลายข้อมูลของคุณ!):
sudo dd if=main.img of=/dev/sdX
เสียบ USB บนคอมพิวเตอร์
เปิด
บอกให้บูตจาก USB
นี่หมายถึงการทำให้เฟิร์มแวร์เลือก USB ก่อนฮาร์ดดิสก์
หากนี่ไม่ใช่พฤติกรรมเริ่มต้นของเครื่องให้กดปุ่ม Enter, F12, ESC หรือปุ่มแปลก ๆ อื่น ๆ หลังจากเปิดเครื่องจนกว่าคุณจะได้รับเมนูบูตที่คุณสามารถเลือกที่จะบูตจาก USB
บ่อยครั้งที่สามารถกำหนดค่าลำดับการค้นหาในเมนูเหล่านั้นได้
ตัวอย่างเช่น Lenovo Thinkpad T430 รุ่นเก่า UEFI BIOS 1.16 ฉันสามารถดู:
สวัสดีชาวโลก
ตอนนี้เราได้จัดทำโปรแกรมขั้นต่ำให้ย้ายไปสู่โลกสวัสดี
คำถามที่ชัดเจนคือทำอย่างไร IO? ตัวเลือกไม่กี่:
- ถามเฟิร์มแวร์เช่น BIOS หรือ UEFI ให้ทำเพื่อเรา
- VGA: พื้นที่หน่วยความจำพิเศษที่ถูกพิมพ์ไปที่หน้าจอถ้าเขียน สามารถใช้ในโหมดที่ได้รับการป้องกัน
- เขียนไดรเวอร์และพูดคุยกับฮาร์ดแวร์การแสดงผลโดยตรง นี่เป็นวิธีที่ "เหมาะสม" ที่จะทำ: มีประสิทธิภาพมากกว่า แต่ซับซ้อนกว่า
พอร์ตอนุกรม นี่เป็นโปรโตคอลมาตรฐานที่ง่ายมากที่ส่งและดึงอักขระจากเทอร์มินัลโฮสต์
แหล่ง
น่าเสียดายที่ไม่ได้เปิดตัวในแล็ปท็อปที่ทันสมัยที่สุด แต่เป็นวิธีการทั่วไปในการพัฒนาบอร์ดดูตัวอย่าง ARM ด้านล่าง
นี่คือจริงๆอัปยศตั้งแต่การเชื่อมต่อดังกล่าวมีประโยชน์จริงๆที่จะแก้ปัญหาเคอร์เนลตัวอย่างเช่น
ใช้คุณสมบัติการดีบักของชิป ARM เรียกพวกเขาsemihostingตัวอย่างเช่น สำหรับฮาร์ดแวร์จริงต้องใช้การสนับสนุนฮาร์ดแวร์และซอฟต์แวร์เพิ่มเติม แต่สำหรับตัวจำลองอาจเป็นตัวเลือกที่สะดวกสบายฟรี ตัวอย่าง
ที่นี่เราจะทำตัวอย่าง BIOS เพราะมันง่ายกว่าบน x86 แต่โปรดทราบว่ามันไม่ใช่วิธีที่แข็งแกร่งที่สุด
main.S
.code16
mov $msg, %si
mov $0x0e, %ah
loop:
lodsb
or %al, %al
jz halt
int $0x10
jmp loop
halt:
hlt
msg:
.asciz "hello world"
link.ld
SECTIONS
{
. = 0x7c00;
.text :
{
__start = .;
*(.text)
. = 0x1FE;
SHORT(0xAA55)
}
}
รวบรวมและเชื่อมโยงกับ:
gcc -c -g -o main.o main.S
ld --oformat binary -o main.img -T linker.ld main.o
ผล:
ทดสอบกับ: Lenovo Thinkpad T430, UEFI BIOS 1.16 ดิสก์สร้างขึ้นบนโฮสต์ Ubuntu 18.04
นอกจากคำแนะนำในการประกอบมาตรฐาน userland แล้วเรายังมี:
.code16
: บอก GAS ให้รับรหัส 16 บิต
cli
: ปิดการใช้งานซอฟต์แวร์ขัดจังหวะ สิ่งเหล่านี้อาจทำให้ตัวประมวลผลเริ่มทำงานอีกครั้งหลังจากนั้นhlt
int $0x10
: ทำการเรียก BIOS นี่คือสิ่งที่พิมพ์อักขระทีละตัว
ธงเชื่อมโยงที่สำคัญคือ:
--oformat binary
: เอาท์พุทรหัสแอสเซมบลีไบนารี่ดิบอย่าบิดมันเข้าไปในไฟล์ ELF ดังที่เป็นกรณีของไฟล์เอ็กซีคิวต์ที่รันได้ปกติ
ใช้ C แทนการชุมนุม
เนื่องจาก C คอมไพล์เป็นแอสเซมบลีการใช้ C โดยไม่มีไลบรารี่มาตรฐานค่อนข้างง่ายคุณจำเป็นเพียงแค่:
- สคริปต์ linker ที่จะนำสิ่งต่าง ๆ ในหน่วยความจำในสถานที่ที่เหมาะสม
- ค่าสถานะที่บอก GCC ว่าจะไม่ใช้ไลบรารีมาตรฐาน
- จุดเริ่มต้นของแอสเซมบลีขนาดเล็กที่ตั้งค่าสถานะ C ที่จำเป็นสำหรับ
main
สะดุดตา:
สิ่งที่ต้องทำ: เชื่อมโยงดังนั้นบางตัวอย่าง x86 ใน GitHub นี่คือ ARM หนึ่งที่ฉันได้สร้างไว้
สิ่งที่สนุกมากขึ้นถ้าคุณต้องการใช้ไลบรารีมาตรฐานเนื่องจากเราไม่มีเคอร์เนล Linux ซึ่งใช้การทำงานของไลบรารี่มาตรฐาน C ผ่าน POSIX
ความเป็นไปได้บางประการโดยไม่ต้องใช้ระบบปฏิบัติการที่เต็มรูปแบบเช่น Linux รวมถึง:
แขน
ใน ARM ความคิดทั่วไปนั้นเหมือนกัน ฉันอัพโหลดแล้ว:
สำหรับ Raspberry Pi https://github.com/dwelch67/raspberrypiดูเหมือนว่าจะมีการสอนที่ได้รับความนิยมมากที่สุดในปัจจุบัน
ความแตกต่างบางอย่างจาก x86 รวมถึง:
IO ทำโดยการเขียนไปยังที่อยู่เวทย์โดยตรงไม่มีin
และout
คำแนะนำ
นี้เรียกว่าหน่วยความจำที่แมป IO
สำหรับฮาร์ดแวร์จริงบางอย่างเช่น Raspberry Pi คุณสามารถเพิ่มเฟิร์มแวร์ (BIOS) เข้ากับภาพดิสก์ได้
นั่นเป็นสิ่งที่ดีเพราะทำให้การอัปเดตเฟิร์มแวร์นั้นโปร่งใสยิ่งขึ้น
เฟิร์มแว
อันที่จริงบูตเซกเตอร์ของคุณไม่ใช่ซอฟต์แวร์ตัวแรกที่ทำงานบน CPU ของระบบ
สิ่งที่ทำงานก่อนจริง ๆ คือเฟิร์มแวร์ที่เรียกว่าซึ่งเป็นซอฟต์แวร์:
- ทำโดยผู้ผลิตฮาร์ดแวร์
- โดยทั่วไปแล้วจะปิดแหล่งที่มา แต่มีแนวโน้มว่า C-based
- เก็บไว้ในหน่วยความจำแบบอ่านอย่างเดียวและยากที่จะแก้ไขโดยไม่ได้รับอนุญาตจากผู้ขาย
เฟิร์มแวร์ที่รู้จักกันดี ได้แก่ :
- ไบออส : เฟิร์มแวร์ x86 ปัจจุบันทั้งหมด SeaBIOS เป็นการใช้งานโอเพ่นซอร์สเริ่มต้นที่ใช้โดย QEMU
- UEFI : ตัวตายตัวแทน BIOS มาตรฐานที่ดีกว่า แต่มีความสามารถมากกว่าและป่องอย่างไม่น่าเชื่อ
- Coreboot : ความพยายามเปิดโอเพนซอร์สอันสูงส่ง
เฟิร์มแวร์ทำสิ่งต่าง ๆ เช่น:
วนรอบฮาร์ดดิสก์แต่ละอัน, USB, เครือข่าย ฯลฯ จนกว่าคุณจะพบสิ่งที่สามารถบู๊ตได้
เมื่อเราเรียกใช้ QEMU -hda
กล่าวว่าmain.img
เป็นฮาร์ดดิสก์ที่เชื่อมต่อกับฮาร์ดแวร์และ
hda
เป็นคนแรกที่จะลองและมีการใช้
โหลด 512 ไบต์แรกไปยังที่อยู่หน่วยความจำ RAM 0x7c00
วาง RIP ของ CPU ที่นั่นและปล่อยให้ทำงาน
แสดงสิ่งต่าง ๆ เช่นเมนูการบูตหรือการเรียกพิมพ์ BIOS บนหน้าจอ
เฟิร์มแวร์มีฟังก์ชั่นคล้ายกับระบบปฏิบัติการที่ OS-es ส่วนใหญ่พึ่งพา เช่นชุดย่อย Python ได้รับการพอร์ตเพื่อให้ทำงานบน BIOS / UEFI: https://www.youtube.com/watch?v=bYQ_lq5dcvM
เป็นที่ถกเถียงกันอยู่ว่าเฟิร์มแวร์นั้นแยกไม่ออกจากระบบปฏิบัติการและเฟิร์มแวร์นั้นเป็นโปรแกรมโลหะเปลือยที่ "จริง" เท่านั้นที่สามารถทำได้
เช่นนี้dev CoreOS ทำให้มัน :
ส่วนที่แข็ง
เมื่อคุณเปิดเครื่องคอมพิวเตอร์ชิปที่ประกอบขึ้นเป็นชิปเซ็ต (Northbridge, Southbridge และ SuperIO) จะยังไม่เริ่มต้นอย่างถูกต้อง แม้ว่า BIOS ROM นั้นจะถูกลบออกไปจากซีพียูเท่าที่จะเป็นไปได้ แต่ซีพียูนี้สามารถเข้าถึงได้เพราะจะต้องมีมิฉะนั้น CPU จะไม่มีคำแนะนำในการดำเนินการ นี่ไม่ได้หมายความว่า BIOS ROM ถูกแมปอย่างสมบูรณ์โดยปกติจะไม่ แต่มีการแมปพอที่จะทำให้กระบวนการบูตดำเนินต่อไป อุปกรณ์อื่น ๆ เพียงแค่ลืมมัน
เมื่อคุณรัน Coreboot ภายใต้ QEMU คุณสามารถทดลองกับ Coreboot ที่สูงขึ้นและ payloads ได้ แต่ QEMU ให้โอกาสเล็กน้อยในการทดสอบด้วยรหัสเริ่มต้นระดับต่ำ สำหรับสิ่งหนึ่ง RAM ทำงานได้ตั้งแต่เริ่มต้น
โพสต์สถานะเริ่มต้น BIOS
เช่นเดียวกับหลาย ๆ สิ่งในฮาร์ดแวร์การสร้างมาตรฐานนั้นอ่อนแอและสิ่งหนึ่งที่คุณไม่ควรพึ่งพาคือสถานะเริ่มต้นของการลงทะเบียนเมื่อรหัสของคุณเริ่มทำงานหลังจาก BIOS
ดังนั้นโปรดทำด้วยตนเองและใช้รหัสเริ่มต้นบางอย่างดังต่อไปนี้: https://stackoverflow.com/a/32509555/895245
การลงทะเบียนที่ชอบ%ds
และ%es
มีผลข้างเคียงที่สำคัญดังนั้นคุณควรเป็นศูนย์ถึงแม้ว่าคุณจะไม่ได้ใช้พวกเขาอย่างชัดเจน
โปรดทราบว่าอีมูเลเตอร์บางตัวนั้นดีกว่าฮาร์ดแวร์จริงและให้สถานะเริ่มต้นที่ดีแก่คุณ จากนั้นเมื่อคุณทำงานบนฮาร์ดแวร์จริงทุกอย่างจะแตก
GNU GRUB Multiboot
บูตเซกเตอร์นั้นง่าย แต่ก็ไม่สะดวก:
- คุณสามารถมีหนึ่งระบบปฏิบัติการต่อดิสก์
- รหัสการโหลดจะต้องมีขนาดเล็กมากและพอดีกับ 512 ไบต์ นี้สามารถแก้ไขได้ด้วยการโทร BIOS int 0x13
- คุณต้องเริ่มต้นด้วยตัวเองบ่อยครั้งเช่นเข้าสู่โหมดที่ได้รับการป้องกัน
มันเป็นเพราะเหตุผลที่GNU GRUBสร้างรูปแบบไฟล์ที่สะดวกกว่าที่เรียกว่า multiboot
ตัวอย่างการทำงานขั้นต่ำ: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world
ฉันยังใช้มันบนrepo ตัวอย่าง GitHubของฉันเพื่อให้สามารถเรียกใช้ตัวอย่างทั้งหมดบนฮาร์ดแวร์จริงได้อย่างง่ายดายโดยไม่ต้องเผา USB เป็นล้านครั้ง ใน QEMU ดูเหมือนว่า:
หากคุณเตรียม OS ของคุณเป็นไฟล์มัลติบูต GRUB สามารถค้นหาได้ในระบบไฟล์ปกติ
นี่คือสิ่งที่ distros ทำส่วนใหญ่วางภาพ OS ไว้ด้าน/boot
ล่าง
ไฟล์มัลติบูตเป็นไฟล์เอลฟ์ที่มีส่วนหัวพิเศษ มีการระบุไว้โดย GRUB ที่: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
คุณสามารถเปิดไฟล์ multiboot grub-mkrescue
เป็นบูตดิสก์ด้วย
เอลโตริโต
รูปแบบที่สามารถเขียนลงซีดีได้: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29
นอกจากนี้ยังสามารถสร้างภาพไฮบริดที่ทำงานได้ทั้ง ISO หรือ USB นี้จะสามารถทำได้ด้วยgrub-mkrescue
( ตัวอย่าง ) และจะทำยังโดยเคอร์เนลในการใช้make isoimage
isohybrid
ทรัพยากร