คุณรันโปรแกรมด้วยตัวเองโดยไม่ต้องใช้ระบบปฏิบัติการได้อย่างไร คุณสามารถสร้างแอสเซมบลีโปรแกรมที่คอมพิวเตอร์สามารถโหลดและรันเมื่อเริ่มต้นเช่นบูตคอมพิวเตอร์จากแฟลชไดรฟ์และรันโปรแกรมที่อยู่บน CPU หรือไม่
คุณรันโปรแกรมด้วยตัวเองโดยไม่ต้องใช้ระบบปฏิบัติการได้อย่างไร คุณสามารถสร้างแอสเซมบลีโปรแกรมที่คอมพิวเตอร์สามารถโหลดและรันเมื่อเริ่มต้นเช่นบูตคอมพิวเตอร์จากแฟลชไดรฟ์และรันโปรแกรมที่อยู่บน CPU หรือไม่
คำตอบ:
คุณรันโปรแกรมด้วยตัวเองโดยไม่ต้องใช้ระบบปฏิบัติการได้อย่างไร
คุณวางรหัสไบนารี่ของคุณไปยังสถานที่ที่หน่วยประมวลผลค้นหาหลังจากรีบูตเครื่อง (เช่นที่อยู่ 0 บน ARM)
คุณสามารถสร้างแอสเซมบลีโปรแกรมที่คอมพิวเตอร์สามารถโหลดและรันเมื่อเริ่มต้น (เช่นบูตคอมพิวเตอร์จากแฟลชไดรฟ์และรันโปรแกรมที่อยู่บนไดรฟ์)
คำตอบทั่วไปของคำถาม: สามารถทำได้ มันมักเรียกว่า "โปรแกรมโลหะเปลือย" หากต้องการอ่านจากแฟลชไดรฟ์คุณต้องการทราบว่า USB คืออะไรและคุณต้องการให้ไดรเวอร์บางตัวทำงานกับ USB นี้ได้ โปรแกรมในไดรฟ์นี้จะต้องอยู่ในรูปแบบเฉพาะบางอย่างในระบบไฟล์บางอย่าง ... นี่คือสิ่งที่บูตโหลดเดอร์มักจะทำ แต่โปรแกรมของคุณอาจรวม bootloader เป็นของตัวเองด้วยดังนั้นหากเฟิร์มแวร์จะ โหลดรหัสขนาดเล็ก
บอร์ด ARM จำนวนมากให้คุณทำสิ่งเหล่านั้น บางคนมีบูตโหลดเดอร์เพื่อช่วยคุณตั้งค่าพื้นฐาน
ที่นี่คุณจะได้พบกับบทแนะนำที่ดีเกี่ยวกับวิธีการทำระบบปฏิบัติการพื้นฐานบน Raspberry Pi
แก้ไข: บทความนี้และ wiki.osdev.org ทั้งหมดจะเป็นคำถามส่วนใหญ่ของคุณ http://wiki.osdev.org/Introduction
นอกจากนี้หากคุณไม่ต้องการทดลองใช้ฮาร์ดแวร์โดยตรงคุณสามารถเรียกใช้เป็นเครื่องเสมือนโดยใช้ไฮเปอร์ไวเซอร์เช่น qemu ดูวิธีการเรียกใช้ "สวัสดีโลก" โดยตรงบนฮาร์ดแวร์ ARM เสมือนจริงที่นี่
ตัวอย่างที่เรียกใช้
มาสร้างและเรียกใช้โปรแกรมโลหะสวัสดีชาวโลกจิ๋วที่ทำงานโดยไม่มีระบบปฏิบัติการบน:
เราจะทดลองใช้โปรแกรมจำลอง QEMU ให้มากที่สุดเท่าที่จะเป็นไปได้ซึ่งจะปลอดภัยและสะดวกยิ่งขึ้นสำหรับการพัฒนา การทดสอบ QEMU นั้นอยู่ในโฮสต์ Ubuntu 18.04 พร้อมกับ QEMU 2.11.1 ที่บรรจุล่วงหน้า
โค้ดของตัวอย่าง x86 ทั้งหมดด้านล่างและอีกมากมายมีอยู่ในGitHub repoนี้
วิธีการเรียกใช้ตัวอย่างบนฮาร์ดแวร์ที่แท้จริง x86
โปรดจำไว้ว่าการใช้งานตัวอย่างบนฮาร์ดแวร์จริงอาจเป็นอันตรายได้เช่นคุณสามารถล้างดิสก์ของคุณหรือก่อกวนฮาร์ดแวร์โดยไม่ตั้งใจ: ทำสิ่งนี้กับเครื่องเก่าที่ไม่มีข้อมูลที่สำคัญ! หรือดียิ่งขึ้นให้ใช้อุปกรณ์เสริมแบบกึ่งทิ้งราคาถูกเช่น Raspberry Pi ดูตัวอย่าง ARM ด้านล่าง
สำหรับแล็ปท็อป x86 ทั่วไปคุณต้องทำสิ่งต่อไปนี้:
เบิร์นภาพเป็นแท่ง USB (จะทำลายข้อมูลของคุณ!):
sudo dd if=main.img of=/dev/sdX
เสียบ USB บนคอมพิวเตอร์
เปิด
บอกให้บูตจาก USB
นี่หมายถึงการทำให้เฟิร์มแวร์เลือก USB ก่อนฮาร์ดดิสก์
หากนั่นไม่ใช่พฤติกรรมเริ่มต้นของเครื่องของคุณให้กดปุ่ม Enter, F12, ESC หรือปุ่มแปลก ๆ อื่น ๆ หลังจากเปิดเครื่องจนกว่าคุณจะได้รับเมนูบูตที่คุณสามารถเลือกที่จะบูตจาก USB
มักจะเป็นไปได้ที่จะกำหนดค่าลำดับการค้นหาในเมนูเหล่านั้น
ตัวอย่างเช่นใน T430 ของฉันฉันเห็นสิ่งต่อไปนี้
หลังจากเปิดเครื่องฉันต้องกด Enter เพื่อเข้าสู่เมนูบู๊ต:
จากนั้นที่นี่ฉันต้องกด F12 เพื่อเลือก USB เป็นอุปกรณ์บูต:
จากตรงนั้นฉันสามารถเลือก USB เป็นอุปกรณ์สำหรับบู๊ตได้ดังนี้:
อีกวิธีหนึ่งในการเปลี่ยนลำดับการบู๊ตและเลือก USB ให้มีลำดับความสำคัญสูงกว่าดังนั้นฉันไม่จำเป็นต้องเลือกด้วยตนเองทุกครั้งฉันจะกด F1 บนหน้าจอ "เมนูเริ่มต้นขัดจังหวะ" แล้วไปที่:
บูตเซกเตอร์
บน 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
ผล:
โปรดทราบว่าแม้จะไม่ได้ทำอะไรเลยตัวละครบางตัวก็พิมพ์อยู่บนหน้าจอแล้ว สิ่งเหล่านี้ถูกพิมพ์โดยเฟิร์มแวร์และให้บริการเพื่อระบุระบบ
และใน T430 เราเพิ่งได้หน้าจอเปล่าพร้อมเคอร์เซอร์กะพริบ:
main.img
มีดังต่อไปนี้:
\364
ใน octal == 0xf4
ใน hex: การเข้ารหัสสำหรับhlt
คำสั่งซึ่งบอกให้ CPU หยุดทำงาน
ดังนั้นโปรแกรมของเราจะไม่ทำอะไรเลยเพียง แต่เริ่มและหยุดเท่านั้น
เราใช้ฐานแปดเพราะเลขฐาน\x
สิบหกไม่ได้ถูกระบุโดย POSIX
เราสามารถรับการเข้ารหัสนี้ได้อย่างง่ายดายด้วย:
echo hlt > a.S
as -o a.o a.S
objdump -S a.o
ผลลัพธ์ใด:
a.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: f4 hlt
แต่มันก็บันทึกไว้ในคู่มือ Intel แน่นอน
%509s
ผลิต 509 ช่องว่าง จำเป็นต้องกรอกข้อมูลในไฟล์จนถึงไบต์ 510
\125\252
ในฐานแปด == ตามมาด้วย0x55
0xaa
เหล่านี้เป็นเวทย์มนตร์ที่จำเป็นต้องใช้ 2 ไบต์ซึ่งต้องเป็นไบต์ 511 และ 512
ไบออสผ่านดิสก์ทั้งหมดของเราเพื่อค้นหาดิสก์ที่สามารถบู๊ตได้และจะพิจารณาเฉพาะดิสก์ที่บู๊ตได้ที่มีเวทย์มนตร์สองไบต์เท่านั้น
หากไม่มีอยู่ฮาร์ดแวร์จะไม่ถือเป็นดิสก์ที่ใช้บู๊ตได้
หากคุณไม่ใช่printf
ผู้เชี่ยวชาญคุณสามารถยืนยันเนื้อหาของmain.img
ด้วย:
hd main.img
ซึ่งแสดงให้เห็นถึงความคาดหวัง:
00000000 f4 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 |. |
00000010 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
*
000001f0 20 20 20 20 20 20 20 20 20 20 20 20 20 20 55 aa | U.|
00000200
โดยที่20
มีช่องว่างใน ASCII
BIOS เฟิร์มแวร์อ่าน 512 ไบต์เหล่านั้นจากดิสก์นำไปไว้ในหน่วยความจำและตั้งค่าพีซีเป็นไบต์แรกเพื่อเริ่มการทำงาน
สวัสดีภาคบูตโลก
ตอนนี้เราได้จัดทำโปรแกรมขั้นต่ำให้ย้ายไปสู่โลกสวัสดี
คำถามที่ชัดเจนคือทำอย่างไร IO? ตัวเลือกไม่กี่:
ถามเฟิร์มแวร์เช่น BIOS หรือ UEFI เพื่อทำเพื่อเรา
VGA: พื้นที่หน่วยความจำพิเศษที่ถูกพิมพ์ไปที่หน้าจอถ้าเขียน สามารถใช้ในโหมดป้องกัน
เขียนไดรเวอร์และพูดคุยกับฮาร์ดแวร์การแสดงผลโดยตรง นี่เป็นวิธีที่ "เหมาะสม" ที่จะทำ: มีประสิทธิภาพมากกว่า แต่ซับซ้อนกว่า
พอร์ตอนุกรม นี่เป็นโปรโตคอลมาตรฐานที่ง่ายมากที่ส่งและรับอักขระจากโฮสต์เทอร์มินัล
บนเดสก์ท็อปดูเหมือนว่า:
น่าเสียดายที่ไม่ได้เปิดตัวในแล็ปท็อปที่ทันสมัยที่สุด แต่เป็นวิธีการทั่วไปในการพัฒนาบอร์ดดูตัวอย่าง ARM ด้านล่าง
นี่คือจริงๆอัปยศตั้งแต่การเชื่อมต่อดังกล่าวมีประโยชน์จริงๆที่จะแก้ปัญหาเคอร์เนลตัวอย่างเช่น
ใช้คุณสมบัติการดีบักของชิป ARM เรียกตัวอย่างการหยุดพักครึ่ง สำหรับฮาร์ดแวร์จริงต้องใช้การสนับสนุนฮาร์ดแวร์และซอฟต์แวร์เพิ่มเติม แต่สำหรับตัวจำลองอาจเป็นตัวเลือกที่สะดวกสบายฟรี ตัวอย่าง
ที่นี่เราจะทำตัวอย่าง 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"
GitHub ต้นน้ำ
link.ld
SECTIONS
{
/* The BIOS loads the code from the disk to this location.
* We must tell that to the linker so that it can properly
* calculate the addresses of symbols we might jump to.
*/
. = 0x7c00;
.text :
{
__start = .;
*(.text)
/* Place the magic boot bytes at the end of the first 512 sector. */
. = 0x1FE;
SHORT(0xAA55)
}
}
รวบรวมและเชื่อมโยงกับ:
as -g -o main.o main.S
ld --oformat binary -o main.img -T link.ld main.o
qemu-system-x86_64 -hda main.img
ผล:
และใน T430:
ทดสอบกับ: Lenovo Thinkpad T430, UEFI BIOS 1.16 ดิสก์ที่สร้างบนโฮสต์ Ubuntu 18.04
นอกจากคำแนะนำในการประกอบมาตรฐาน userland แล้วเรายังมี:
.code16
: บอก GAS ให้รับรหัส 16 บิต
cli
: ปิดการใช้งานซอฟต์แวร์ขัดจังหวะ สิ่งเหล่านี้อาจทำให้ตัวประมวลผลเริ่มทำงานอีกครั้งหลังจากนั้นhlt
int $0x10
: ทำการเรียก BIOS นี่คือสิ่งที่พิมพ์อักขระทีละตัว
ธงเชื่อมโยงที่สำคัญคือ:
--oformat binary
: เอาท์พุทรหัสแอสเซมบลีไบนารี่ดิบอย่าห่อไว้ในไฟล์ ELF ดังที่เป็นกรณีของไฟล์เอ็กซีคิวต์ที่รันได้ปกติเพื่อให้เข้าใจส่วนสคริปต์ตัวเชื่อมโยงได้ดียิ่งขึ้นให้ทำความคุ้นเคยกับขั้นตอนการเปลี่ยนตำแหน่งของการเชื่อมโยง: ตัวเชื่อมโยงทำอะไร
โปรแกรมโลหะเปลือย x86 ที่เย็นกว่า
ต่อไปนี้เป็นตัวอย่างการตั้งค่าโลหะเปลือยที่ซับซ้อนยิ่งขึ้นที่ฉันได้รับ:
ใช้ C แทนการชุมนุม
สรุป: ใช้ GRUB multiboot ซึ่งจะแก้ปัญหาที่น่ารำคาญมากมายที่คุณไม่เคยคิดถึง ดูส่วนด้านล่าง
ปัญหาหลักใน x86 คือ BIOS โหลดได้เพียง 512 ไบต์จากดิสก์ไปยังหน่วยความจำและคุณมีแนวโน้มที่จะระเบิด 512 ไบต์เหล่านั้นเมื่อใช้ C!
เพื่อแก้ปัญหาที่เราสามารถใช้bootloader สองขั้นตอน สิ่งนี้ทำให้การเรียก BIOS เพิ่มเติมซึ่งโหลดไบต์เพิ่มเติมจากดิสก์ลงในหน่วยความจำ นี่คือตัวอย่างขั้นตอนการประกอบขั้นต่ำ 2 น้อยที่สุดตั้งแต่เริ่มต้นโดยใช้การเรียกint 0x13 BIOS :
อีกวิธีหนึ่งคือ:
-kernel
ตัวเลือกซึ่งโหลดไฟล์ ELF ทั้งหมดลงในหน่วยความจำ นี่คือตัวอย่าง ARM ที่ฉันสร้างด้วยวิธีการนั้นkernel7.img
เหมือนกับที่ QEMU -kernel
ทำเพื่อการศึกษาเท่านั้นนี่คือตัวอย่างขั้นต่ำ C ขั้นตอนเดียว :
main.c
void main(void) {
int i;
char s[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
for (i = 0; i < sizeof(s); ++i) {
__asm__ (
"int $0x10" : : "a" ((0x0e << 8) | s[i])
);
}
while (1) {
__asm__ ("hlt");
};
}
entry.s ของ
.code16
.text
.global mystart
mystart:
ljmp $0, $.setcs
.setcs:
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
mov $__stack_top, %esp
cld
call main
linker.ld
ENTRY(mystart)
SECTIONS
{
. = 0x7c00;
.text : {
entry.o(.text)
*(.text)
*(.data)
*(.rodata)
__bss_start = .;
/* COMMON vs BSS: /programming/16835716/bss-vs-common-what-goes-where */
*(.bss)
*(COMMON)
__bss_end = .;
}
/* /programming/53584666/why-does-gnu-ld-include-a-section-that-does-not-appear-in-the-linker-script */
.sig : AT(ADDR(.text) + 512 - 2)
{
SHORT(0xaa55);
}
/DISCARD/ : {
*(.eh_frame)
}
__stack_bottom = .;
. = . + 0x1000;
__stack_top = .;
}
วิ่ง
set -eux
as -ggdb3 --32 -o entry.o entry.S
gcc -c -ggdb3 -m16 -ffreestanding -fno-PIE -nostartfiles -nostdlib -o main.o -std=c99 main.c
ld -m elf_i386 -o main.elf -T linker.ld entry.o main.o
objcopy -O binary main.elf main.img
qemu-system-x86_64 -drive file=main.img,format=raw
C ไลบรารีมาตรฐาน
สิ่งที่ได้รับความสนุกสนานมากขึ้นถ้าคุณยังต้องการที่จะใช้ห้องสมุดมาตรฐาน C แต่เนื่องจากเราไม่ได้มีลินุกซ์เคอร์เนลซึ่งดำเนินการมากของการทำงานห้องสมุด C มาตรฐานผ่าน POSIX
ความเป็นไปได้บางประการโดยไม่ต้องใช้ระบบปฏิบัติการที่เต็มรูปแบบเช่น Linux รวมถึง:
เขียนของคุณเอง เป็นเพียงส่วนหัวและไฟล์ C ในที่สุดใช่ไหม ขวา??
ตัวอย่างโดยละเอียดที่: /electronics/223929/c-standard-l ไลบรารี-on-bare-metal/223931
การดำเนินการ Newlib ทุกสิ่งที่น่าเบื่อไม่ใช่ OS เฉพาะสำหรับคุณเช่นmemcmp
, memcpy
ฯลฯ
จากนั้นจะให้ส่วนสมบูรณ์สำหรับคุณในการติดตั้ง syscalls ที่คุณต้องการด้วยตัวคุณเอง
ตัวอย่างเช่นเราสามารถนำไปใช้exit()
กับ ARM ผ่านการเซมาสต์ด้วย:
void _exit(int status) {
__asm__ __volatile__ ("mov r0, #0x18; ldr r1, =#0x20026; svc 0x00123456");
}
ดังที่แสดงในตัวอย่างนี้
ตัวอย่างเช่นคุณสามารถเปลี่ยนเส้นทางprintf
ไปยัง UART หรือ ARM ระบบหรือดำเนินการexit()
กับsemihosting
ฝังตัวระบบปฏิบัติการเช่นFreeRTOSและZephyr
โดยทั่วไประบบปฏิบัติการดังกล่าวจะช่วยให้คุณสามารถปิดการกำหนดเวลาล่วงหน้าได้ดังนั้นคุณจึงสามารถควบคุมการทำงานของโปรแกรมได้อย่างสมบูรณ์
พวกเขาสามารถมองเห็นเป็น Newlib ก่อนดำเนินการ
GNU GRUB Multiboot
บูตเซกเตอร์นั้นง่าย แต่ก็ไม่สะดวก:
มันเป็นเพราะเหตุผลเหล่านั้นGNU GRUBสร้างรูปแบบไฟล์ที่สะดวกกว่าที่เรียกว่า multiboot
ตัวอย่างการทำงานขั้นต่ำ: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world
ฉันยังใช้มันในrepo ตัวอย่าง GitHubของฉันเพื่อให้สามารถเรียกใช้ตัวอย่างทั้งหมดบนฮาร์ดแวร์จริงได้อย่างง่ายดายโดยไม่ต้องเผา USB เป็นล้านครั้ง
ผลลัพธ์ QEMU:
T430:
หากคุณเตรียม OS ของคุณเป็นไฟล์มัลติบูต GRUB จะสามารถค้นหาได้ในระบบไฟล์ปกติ
นี่คือสิ่งที่ distros ทำส่วนใหญ่วางภาพ OS ไว้ด้าน/boot
ล่าง
ไฟล์มัลติบูตเป็นไฟล์เอลฟ์ที่มีส่วนหัวพิเศษ มีการระบุโดย GRUB ที่: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
คุณสามารถเปิดไฟล์ multiboot grub-mkrescue
ลงในแผ่นดิสก์บูตด้วย
เฟิร์มแว
อันที่จริงบูตเซกเตอร์ของคุณไม่ใช่ซอฟต์แวร์ตัวแรกที่ทำงานบน CPU ของระบบ
สิ่งที่ทำงานก่อนจริง ๆ คือเฟิร์มแวร์ที่เรียกว่าซึ่งเป็นซอฟต์แวร์:
เฟิร์มแวร์ที่รู้จักกันดี ได้แก่ :
เฟิร์มแวร์ทำสิ่งต่าง ๆ เช่น:
วนรอบฮาร์ดดิสก์แต่ละอัน, 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
เป็นที่ถกเถียงกันอยู่ว่าเฟิร์มแวร์นั้นแยกไม่ออกจากระบบปฏิบัติการและเฟิร์มแวร์นั้นเป็นโปรแกรมโลหะเปลือยที่ "จริง" เท่านั้นที่ทำได้
เช่นนี้CoreOS dev ทำให้มัน :
ส่วนที่แข็ง
เมื่อคุณเปิดเครื่องคอมพิวเตอร์ชิปที่ประกอบขึ้นเป็นชิปเซ็ต (Northbridge, Southbridge และ SuperIO) จะยังไม่เริ่มต้นอย่างถูกต้อง แม้ว่า BIOS ROM จะถูกลบออกไปไกลจากซีพียูเท่าที่จะเป็นไปได้ แต่สิ่งนี้สามารถเข้าถึงได้โดยซีพียูเพราะจะต้องมีมิฉะนั้น CPU จะไม่มีคำแนะนำในการดำเนินการ นี่ไม่ได้หมายความว่า BIOS ROM ถูกแมปอย่างสมบูรณ์โดยปกติจะไม่ แต่มีการแมปพอที่จะทำให้กระบวนการบูตดำเนินต่อไป อุปกรณ์อื่น ๆ เพียงแค่ลืมมัน
เมื่อคุณเรียกใช้ Coreboot ภายใต้ QEMU คุณสามารถทดสอบกับ Coreboot ในระดับที่สูงขึ้นและด้วย payloads แต่ QEMU ให้โอกาสเล็กน้อยในการทดสอบด้วยรหัสเริ่มต้นระดับต่ำ สำหรับสิ่งหนึ่ง RAM ทำงานได้ตั้งแต่เริ่มต้น
โพสต์สถานะเริ่มต้น BIOS
เช่นเดียวกับหลาย ๆ สิ่งในฮาร์ดแวร์การสร้างมาตรฐานนั้นอ่อนแอและสิ่งหนึ่งที่คุณไม่ควรพึ่งพาคือสถานะเริ่มต้นของการลงทะเบียนเมื่อรหัสของคุณเริ่มทำงานหลังจาก BIOS
ดังนั้นโปรดทำด้วยตนเองและใช้รหัสเริ่มต้นบางอย่างดังนี้: https://stackoverflow.com/a/32509555/895245
การลงทะเบียนเช่น%ds
และ%es
มีผลข้างเคียงที่สำคัญดังนั้นคุณควรเป็นศูนย์ถึงแม้ว่าคุณจะไม่ได้ใช้พวกเขาอย่างชัดเจน
โปรดทราบว่าอีมูเลเตอร์บางตัวนั้นดีกว่าฮาร์ดแวร์จริงและให้สถานะเริ่มต้นที่ดีแก่คุณ จากนั้นเมื่อคุณทำงานบนฮาร์ดแวร์จริงทุกอย่างจะแตก
เอลโตริโต
รูปแบบที่สามารถเขียนลงซีดีได้: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29
นอกจากนี้ยังสามารถสร้างภาพไฮบริดที่ทำงานได้ทั้ง ISO หรือ USB นี้จะสามารถทำได้ด้วยgrub-mkrescue
( ตัวอย่าง ) และจะทำยังโดยลินุกซ์เคอร์เนลในการใช้make isoimage
isohybrid
แขน
ใน ARM ความคิดทั่วไปนั้นเหมือนกัน
ไม่มีเฟิร์มแวร์ที่ติดตั้งมาล่วงหน้าแบบกึ่งมาตรฐานเช่น BIOS สำหรับเราที่จะใช้สำหรับ IO ดังนั้น IO ที่ง่ายที่สุดสองประเภทที่เราสามารถทำได้คือ:
ฉันอัพโหลดแล้ว:
ไม่กี่ง่าย QEMU C + Newlib และตัวอย่างประกอบดิบนี่บน GitHub
ตัวอย่าง prompt.cตัวอย่างเช่นจะเข้าจากสถานีโฮสต์ของคุณและช่วยให้การส่งออกกลับมาทั้งหมดผ่าน UART จำลอง:
enter a character
got: a
new alloc of 1 bytes at address 0x0x4000a1c0
enter a character
got: b
new alloc of 2 bytes at address 0x0x4000a1c0
enter a character
ดูเพิ่มเติม: วิธีทำโปรแกรม ARM โลหะเปลือยและใช้งานใน QEMU?
การตั้งค่าไฟกระพริบอัตโนมัติ Raspberry Pi โดยสมบูรณ์ที่: https://github.com/cirosantilli/raspberry-pi-bare-metal-blinker
ดูเพิ่มเติม: วิธีรันโปรแกรม C ที่ไม่มีระบบปฏิบัติการบน Raspberry Pi
หากต้องการ "ดู" ไฟ LED บน QEMU คุณต้องรวบรวม QEMU จากแหล่งที่มาด้วยการตั้งค่าสถานะการดีบัก: /raspberrypi/56373/is-it-possible-to-get-the-state-of- ที่ไฟ LED และ GPIOs-in-a-qemu-จำลองเหมือน-T
ถัดไปคุณควรลอง UART สวัสดีชาวโลก คุณสามารถเริ่มต้นจากตัวอย่างตัวบ่งชี้และแทนที่เคอร์เนลด้วยอันนี้: https://github.com/dwelch67/raspberrypi/tree/bce377230c2cdd8ff1e40919fdedbc2533ef5a00/uart01
อันดับแรกให้ UART ทำงานร่วมกับ Raspbian ดังที่ฉันได้อธิบายไว้ที่: /raspberrypi/38/prepare-for-ssh-without-a-screen/54394#54394สิ่งนี้จะมีลักษณะเช่นนี้:
ตรวจสอบให้แน่ใจว่าใช้พินที่ถูกต้องมิฉะนั้นคุณสามารถเบิร์น UART เป็นตัวแปลง USB ได้ฉันทำไปแล้วสองครั้งโดยการลัดวงจรและ 5V ...
ในที่สุดเชื่อมต่อกับอนุกรมจากโฮสต์ด้วย:
screen /dev/ttyUSB0 115200
สำหรับ Raspberry Pi เราใช้การ์ด Micro SD แทน USB stick เพื่อเก็บไฟล์ปฏิบัติการของเราซึ่งโดยปกติคุณต้องใช้อะแดปเตอร์เพื่อเชื่อมต่อกับคอมพิวเตอร์ของคุณ:
อย่าลืมปลดล็อคอะแดปเตอร์ SD ดังที่แสดงไว้ที่: /ubuntu/213889/microsd-card-is-set-to-read-only-state-how-can-i-write-data -ON มัน / 814585 # 814585
https://github.com/dwelch67/raspberrypiดูเหมือนว่าการสอน Raspberry Pi โลหะเปลือยที่เป็นที่นิยมมากที่สุดที่มีในปัจจุบัน
ความแตกต่างบางอย่างจาก x86 รวมถึง:
IO ทำโดยการเขียนไปยังที่อยู่เวทย์โดยตรงไม่มีin
และout
คำแนะนำ
นี้เรียกว่าหน่วยความจำที่แมป IO
สำหรับฮาร์ดแวร์จริงบางอย่างเช่น Raspberry Pi คุณสามารถเพิ่มเฟิร์มแวร์ (BIOS) เข้ากับภาพดิสก์ได้
นั่นเป็นสิ่งที่ดีเนื่องจากทำให้การอัปเดตเฟิร์มแวร์นั้นโปร่งใสยิ่งขึ้น
ทรัพยากร
ระบบปฏิบัติการยังเป็นโปรแกรมดังนั้นเราจึงสามารถสร้างโปรแกรมของเราเองได้ด้วยการสร้างตั้งแต่เริ่มต้นหรือเปลี่ยนแปลง (จำกัด หรือเพิ่ม) คุณสมบัติของระบบปฏิบัติการขนาดเล็กจากนั้นเรียกใช้ระหว่างกระบวนการบู๊ต (โดยใช้อิมเมจ ISO ) .
ตัวอย่างเช่นหน้านี้สามารถใช้เป็นจุดเริ่มต้น:
วิธีเขียนระบบปฏิบัติการอย่างง่าย
ที่นี่ระบบปฏิบัติการทั้งหมดพอดีกับบูตเซกเตอร์ขนาด 512 ไบต์ ( MBR )!
OS ที่เรียบง่ายดังกล่าวหรือคล้ายกันสามารถใช้ในการสร้างกรอบงานง่าย ๆ ที่จะช่วยให้เรา:
ทำให้ภาระ bootloader ภาคต่อมาบนดิสก์เข้าไปในแรมและข้ามไปยังจุดที่จะดำเนินการต่อการดำเนินการ หรือคุณอาจจะอ่านข้อมูลเกี่ยวกับ FAT12, ระบบไฟล์ที่ใช้ในไดรฟ์ฟลอปปี้และดำเนินการที่
อย่างไรก็ตามมีความเป็นไปได้มากมาย ตัวอย่างเช่นเพื่อดูภาษาแอสเซมบลี x86 ที่ใหญ่กว่าเราสามารถสำรวจMykeOS , ระบบปฏิบัติการ x86 ซึ่งเป็นเครื่องมือการเรียนรู้เพื่อแสดงระบบปฏิบัติการจริงแบบ 16 บิตที่เรียบง่ายพร้อมรหัสที่มีความคิดเห็นดีและเอกสารประกอบมากมาย
โปรแกรมประเภททั่วไปอื่น ๆที่ทำงานโดยไม่มีระบบปฏิบัติการก็เป็น Boot Loadersเช่นกัน เราสามารถสร้างโปรแกรมที่ได้รับแรงบันดาลใจจากแนวคิดดังกล่าวตัวอย่างเช่นการใช้เว็บไซต์นี้:
วิธีการพัฒนา Boot Loader ของคุณเอง
บทความข้างต้นแสดงถึงสถาปัตยกรรมพื้นฐานของโปรแกรมดังกล่าว :
- แก้ไขการโหลดหน่วยความจำด้วย 0000: 7C00 address
- การเรียกใช้ฟังก์ชัน BootMainที่พัฒนาขึ้นในภาษาระดับสูง
- แสดงข้อความ“” สวัสดีโลก…” จากระดับต่ำ” บนหน้าจอ
ดังที่เราเห็นสถาปัตยกรรมนี้มีความยืดหยุ่นสูงและช่วยให้เราสามารถใช้งานโปรแกรมใด ๆไม่จำเป็นต้องเป็นบูตโหลดเดอร์
โดยเฉพาะอย่างยิ่งมันแสดงให้เห็นถึงวิธีการใช้เทคนิค "รหัสผสม"ซึ่งเป็นไปได้ที่จะรวมการก่อสร้างระดับสูง (จากCหรือC ++ ) กับคำสั่งระดับต่ำ (จากAssembler ) นี่เป็นวิธีที่มีประโยชน์มาก แต่เราต้องจำไว้ว่า:
การสร้างโปรแกรมและได้รับแฟ้มที่ปฏิบัติการที่คุณจะต้องคอมไพเลอร์และลิงเกอร์ของ Assembler สำหรับโหมด สำหรับ C / C ++คุณจะต้องเพียงคอมไพเลอร์ที่สามารถสร้างไฟล์วัตถุสำหรับโหมด 16 บิต
บทความนี้ยังแสดงวิธีดูโปรแกรมที่สร้างขึ้นในขณะใช้งานและวิธีการทดสอบและดีบัก
ตัวอย่างข้างต้นใช้ข้อเท็จจริงของการโหลดเซกเตอร์ MBR บนสื่อข้อมูล อย่างไรก็ตามเราสามารถเข้าไปในส่วนลึกได้โดยการวางตัวอย่างเช่นกับแอปพลิเคชันUEFI :
นอกเหนือจากการโหลดระบบปฏิบัติการแล้ว UEFI สามารถเรียกใช้แอปพลิเคชัน UEFI ซึ่งอยู่ในไฟล์บนพาร์ติชันระบบ EFI สามารถดำเนินการได้จากเชลล์คำสั่ง UEFI โดยตัวจัดการการบูตของเฟิร์มแวร์หรือโดยแอปพลิเคชัน UEFI อื่น ๆ แอปพลิเคชัน UEFI สามารถพัฒนาและติดตั้งได้อย่างอิสระจากผู้ผลิตระบบ
แอปพลิเคชั่นประเภท UEFI เป็นตัวโหลดระบบปฏิบัติการเช่น GRUB, rEFInd, Gummiboot และ Windows Boot Manager ซึ่งโหลดไฟล์ OS ลงในหน่วยความจำและดำเนินการ นอกจากนี้ตัวโหลด OS สามารถจัดเตรียมส่วนต่อประสานกับผู้ใช้เพื่อให้สามารถเลือกแอปพลิเคชัน UEFI อื่นให้ทำงานได้ ยูทิลิตี้เช่นเปลือก UEFI เป็นแอปพลิเคชัน UEFI
หากเราต้องการเริ่มสร้างโปรแกรมดังกล่าวเราสามารถเริ่มต้นด้วยเว็บไซต์เหล่านี้ได้:
การเขียนโปรแกรมสำหรับ EFI: การสร้างโปรแกรม "Hello, World" / UEFI - ขั้นตอนแรก
เป็นที่ทราบกันดีว่ามีซอฟต์แวร์ที่เป็นอันตรายทั้งกลุ่ม(ซึ่งเป็นโปรแกรม) ที่ใช้งานอยู่ก่อนที่ระบบปฏิบัติการจะเริ่มต้นขึ้น
กลุ่มใหญ่ของพวกเขาทำงานในภาค MBR หรือแอพพลิเคชั่น UEFI เช่นเดียวกับโซลูชั่นทั้งหมดข้างต้น แต่ยังมีกลุ่มที่ใช้จุดเริ่มต้นอื่นเช่นVolume Boot Record (VBR) หรือBIOS :
มีไวรัสโจมตี BIOS ที่รู้จักกันอย่างน้อยสี่ตัวโดยมีสองตัวเพื่อวัตถุประสงค์ในการสาธิต
หรือบางทีอีกคนหนึ่งด้วย
Bootkitsมีวิวัฒนาการมาจากหลักฐานของแนวคิดการพัฒนาเพื่อการกระจายมวลและในขณะนี้ได้อย่างมีประสิทธิภาพกลายเป็นซอฟต์แวร์โอเพนซอร์ส
ผมยังคิดว่าในบริบทนี้มันก็ยังเป็นมูลค่าการกล่าวขวัญว่ามีรูปแบบต่างๆของการบูตระบบปฏิบัติการ (หรือโปรแกรมปฏิบัติการไว้สำหรับการนี้) มีเป็นจำนวนมาก แต่ผมอยากจะให้ความสนใจในการโหลดรหัสจากเครือข่ายโดยใช้เครือข่าย Boot ตัวเลือก ( PXE ) ซึ่งช่วยให้เราสามารถเรียกใช้โปรแกรมบนเครื่องคอมพิวเตอร์โดยไม่คำนึงถึงระบบปฏิบัติการและแม้กระทั่งโดยไม่คำนึงถึงสื่อเก็บข้อมูลใด ๆที่เป็น เชื่อมต่อโดยตรงกับคอมพิวเตอร์: