จะรันโปรแกรมโดยไม่มีระบบปฏิบัติการได้อย่างไร?


239

คุณรันโปรแกรมด้วยตัวเองโดยไม่ต้องใช้ระบบปฏิบัติการได้อย่างไร คุณสามารถสร้างแอสเซมบลีโปรแกรมที่คอมพิวเตอร์สามารถโหลดและรันเมื่อเริ่มต้นเช่นบูตคอมพิวเตอร์จากแฟลชไดรฟ์และรันโปรแกรมที่อยู่บน CPU หรือไม่


4
สถาปัตยกรรมแบบไหน x86? แขน?
Kissiel

1
ฉันพูดทั่วไป แต่มีแนวโน้มมากที่ x86 หรือ x64
2320609

2
ใช่นั่นเป็นวิธีที่โปรเซสเซอร์ดำเนินการอย่างแน่นอน ไม่จำเป็นต้องมีการประกอบ, C มักจะใช้กับ asm เล็กน้อยสำหรับ bootstrap และอาจมีการสนับสนุนอื่น ๆ
old_timer

24
ลองคิดดู: หากไม่มีความสามารถดังกล่าวระบบปฏิบัติการของตัวเองจะเริ่มต้นและทำงานได้อย่างไร? :)
Seva Alekseyev

คำตอบ:


153

คุณรันโปรแกรมด้วยตัวเองโดยไม่ต้องใช้ระบบปฏิบัติการได้อย่างไร

คุณวางรหัสไบนารี่ของคุณไปยังสถานที่ที่หน่วยประมวลผลค้นหาหลังจากรีบูตเครื่อง (เช่นที่อยู่ 0 บน ARM)

คุณสามารถสร้างแอสเซมบลีโปรแกรมที่คอมพิวเตอร์สามารถโหลดและรันเมื่อเริ่มต้น (เช่นบูตคอมพิวเตอร์จากแฟลชไดรฟ์และรันโปรแกรมที่อยู่บนไดรฟ์)

คำตอบทั่วไปของคำถาม: สามารถทำได้ มันมักเรียกว่า "โปรแกรมโลหะเปลือย" หากต้องการอ่านจากแฟลชไดรฟ์คุณต้องการทราบว่า USB คืออะไรและคุณต้องการให้ไดรเวอร์บางตัวทำงานกับ USB นี้ได้ โปรแกรมในไดรฟ์นี้จะต้องอยู่ในรูปแบบเฉพาะบางอย่างในระบบไฟล์บางอย่าง ... นี่คือสิ่งที่บูตโหลดเดอร์มักจะทำ แต่โปรแกรมของคุณอาจรวม bootloader เป็นของตัวเองด้วยดังนั้นหากเฟิร์มแวร์จะ โหลดรหัสขนาดเล็ก

บอร์ด ARM จำนวนมากให้คุณทำสิ่งเหล่านั้น บางคนมีบูตโหลดเดอร์เพื่อช่วยคุณตั้งค่าพื้นฐาน

ที่นี่คุณจะได้พบกับบทแนะนำที่ดีเกี่ยวกับวิธีการทำระบบปฏิบัติการพื้นฐานบน Raspberry Pi

แก้ไข: บทความนี้และ wiki.osdev.org ทั้งหมดจะเป็นคำถามส่วนใหญ่ของคุณ http://wiki.osdev.org/Introduction

นอกจากนี้หากคุณไม่ต้องการทดลองใช้ฮาร์ดแวร์โดยตรงคุณสามารถเรียกใช้เป็นเครื่องเสมือนโดยใช้ไฮเปอร์ไวเซอร์เช่น qemu ดูวิธีการเรียกใช้ "สวัสดีโลก" โดยตรงบนฮาร์ดแวร์ ARM เสมือนจริงที่นี่


723

ตัวอย่างที่เรียกใช้

มาสร้างและเรียกใช้โปรแกรมโลหะสวัสดีชาวโลกจิ๋วที่ทำงานโดยไม่มีระบบปฏิบัติการบน:

เราจะทดลองใช้โปรแกรมจำลอง QEMU ให้มากที่สุดเท่าที่จะเป็นไปได้ซึ่งจะปลอดภัยและสะดวกยิ่งขึ้นสำหรับการพัฒนา การทดสอบ QEMU นั้นอยู่ในโฮสต์ Ubuntu 18.04 พร้อมกับ QEMU 2.11.1 ที่บรรจุล่วงหน้า

โค้ดของตัวอย่าง x86 ทั้งหมดด้านล่างและอีกมากมายมีอยู่ในGitHub repoนี้

วิธีการเรียกใช้ตัวอย่างบนฮาร์ดแวร์ที่แท้จริง x86

โปรดจำไว้ว่าการใช้งานตัวอย่างบนฮาร์ดแวร์จริงอาจเป็นอันตรายได้เช่นคุณสามารถล้างดิสก์ของคุณหรือก่อกวนฮาร์ดแวร์โดยไม่ตั้งใจ: ทำสิ่งนี้กับเครื่องเก่าที่ไม่มีข้อมูลที่สำคัญ! หรือดียิ่งขึ้นให้ใช้อุปกรณ์เสริมแบบกึ่งทิ้งราคาถูกเช่น Raspberry Pi ดูตัวอย่าง ARM ด้านล่าง

สำหรับแล็ปท็อป x86 ทั่วไปคุณต้องทำสิ่งต่อไปนี้:

  1. เบิร์นภาพเป็นแท่ง USB (จะทำลายข้อมูลของคุณ!):

    sudo dd if=main.img of=/dev/sdX
    
  2. เสียบ USB บนคอมพิวเตอร์

  3. เปิด

  4. บอกให้บูตจาก 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ในฐานแปด == ตามมาด้วย0x550xaa

    เหล่านี้เป็นเวทย์มนตร์ที่จำเป็นต้องใช้ 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 :

อีกวิธีหนึ่งคือ:

  • หากคุณต้องการใช้งานใน QEMU แต่ไม่ใช่ฮาร์ดแวร์จริงให้ใช้-kernelตัวเลือกซึ่งโหลดไฟล์ ELF ทั้งหมดลงในหน่วยความจำ นี่คือตัวอย่าง ARM ที่ฉันสร้างด้วยวิธีการนั้น
  • สำหรับ Raspberry Pi เฟิร์มแวร์เริ่มต้นจะดูแลการโหลดรูปภาพจากไฟล์ ELF ที่เราตั้งชื่อ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 ในที่สุดใช่ไหม ขวา??

  • Newlib

    ตัวอย่างโดยละเอียดที่: /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

บูตเซกเตอร์นั้นง่าย แต่ก็ไม่สะดวก:

  • คุณสามารถมีหนึ่งระบบปฏิบัติการต่อดิสก์
  • รหัสการโหลดจะต้องมีขนาดเล็กมากและพอดีกับ 512 ไบต์
  • คุณต้องเริ่มต้นด้วยตัวเองบ่อยครั้งเช่นเข้าสู่โหมดป้องกัน

มันเป็นเพราะเหตุผลเหล่านั้น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 ของระบบ

สิ่งที่ทำงานก่อนจริง ๆ คือเฟิร์มแวร์ที่เรียกว่าซึ่งเป็นซอฟต์แวร์:

  • ทำโดยผู้ผลิตฮาร์ดแวร์
  • โดยทั่วไปแล้วจะปิดแหล่งที่มา แต่มีแนวโน้มว่า C-based
  • เก็บไว้ในหน่วยความจำแบบอ่านอย่างเดียวและยากที่จะแก้ไขโดยไม่ได้รับอนุญาตจากผู้ขาย

เฟิร์มแวร์ที่รู้จักกันดี ได้แก่ :

  • BIOS : เฟิร์มแวร์ 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

เป็นที่ถกเถียงกันอยู่ว่าเฟิร์มแวร์นั้นแยกไม่ออกจากระบบปฏิบัติการและเฟิร์มแวร์นั้นเป็นโปรแกรมโลหะเปลือยที่ "จริง" เท่านั้นที่ทำได้

เช่นนี้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 isoimageisohybrid

แขน

ใน ARM ความคิดทั่วไปนั้นเหมือนกัน

ไม่มีเฟิร์มแวร์ที่ติดตั้งมาล่วงหน้าแบบกึ่งมาตรฐานเช่น BIOS สำหรับเราที่จะใช้สำหรับ IO ดังนั้น IO ที่ง่ายที่สุดสองประเภทที่เราสามารถทำได้คือ:

  • อนุกรมซึ่งมีอยู่อย่างแพร่หลายใน devboards
  • กะพริบไฟ LED

ฉันอัพโหลดแล้ว:

ความแตกต่างบางอย่างจาก x86 รวมถึง:

  • IO ทำโดยการเขียนไปยังที่อยู่เวทย์โดยตรงไม่มีinและoutคำแนะนำ

    นี้เรียกว่าหน่วยความจำที่แมป IO

  • สำหรับฮาร์ดแวร์จริงบางอย่างเช่น Raspberry Pi คุณสามารถเพิ่มเฟิร์มแวร์ (BIOS) เข้ากับภาพดิสก์ได้

    นั่นเป็นสิ่งที่ดีเนื่องจากทำให้การอัปเดตเฟิร์มแวร์นั้นโปร่งใสยิ่งขึ้น

ทรัพยากร

  • http://wiki.osdev.orgเป็นแหล่งข้อมูลที่ดีสำหรับเรื่องเหล่านั้น
  • https://github.com/scanlime/metalkitเป็นระบบรวบรวมโลหะอัตโนมัติ / ทั่วไปที่ให้ API แบบกำหนดเองขนาดเล็ก

3
Unikernelsเป็นทางเลือกสำหรับคนที่ไม่สามารถ / ไม่ต้องการที่จะไปในระดับต่ำและยังต้องการได้รับประโยชน์จากรอยเท้าต่ำของพวกเขา
AndreLDM

1
@AndreLDM ผมอยู่บนปากเหวของการเพิ่มว่า Linux ตามข่าว Unikernel แต่รู้สึกหงุดหงิดมากเกินไปเลย: next.redhat.com/2018/11/14/ukl-a-unikernel-based-on-linux
Ciro Santilli郝海东冠状病六四事件法轮功

14
คำตอบโดยละเอียดจริงๆ แต่ "โปรแกรมที่ทำงานโดยไม่มีระบบปฏิบัติการเป็นระบบปฏิบัติการ" ไม่เป็นความจริง คุณสามารถเขียนโปรแกรมที่เพิ่งเปิด / ปิดไฟ LED แต่ไม่ได้ทำให้เป็นระบบปฏิบัติการ รหัสเฟิร์มแวร์บางตัวที่เรียกใช้ไมโครคอนโทรลเลอร์บนแฟลชไดรฟ์ของคุณไม่ได้ทำให้เป็นระบบปฏิบัติการ ระบบปฏิบัติการเป็นเลเยอร์นามธรรมอย่างน้อยที่สุดเพื่อเขียนซอฟต์แวร์อื่น ๆ ได้ง่ายขึ้น อย่างน้อยที่สุดวันนี้ฉันจะบอกว่าไม่มีตัวจัดตารางเวลามันอาจจะไม่ใช่ระบบปฏิบัติการ
Vitali

4
คำตอบที่ดียกเว้นเรื่องไร้สาระที่แน่นอนว่าโปรแกรมใด ๆ ที่ไม่ทำงานใน OS เป็นระบบปฏิบัติการ
อยากรู้อยากเห็น dannii

3
@MichaelPetch เฮ้เพียงเพื่อบันทึก null ในบูตเซกเตอร์ :-) ไม่น่าจะคุ้มค่า
Ciro Santilli 郝海东冠状病六四事件法轮功

3

ระบบปฏิบัติการเป็นแรงบันดาลใจ

ระบบปฏิบัติการยังเป็นโปรแกรมดังนั้นเราจึงสามารถสร้างโปรแกรมของเราเองได้ด้วยการสร้างตั้งแต่เริ่มต้นหรือเปลี่ยนแปลง (จำกัด หรือเพิ่ม) คุณสมบัติของระบบปฏิบัติการขนาดเล็กจากนั้นเรียกใช้ระหว่างกระบวนการบู๊ต (โดยใช้อิมเมจ ISO ) .

ตัวอย่างเช่นหน้านี้สามารถใช้เป็นจุดเริ่มต้น:

วิธีเขียนระบบปฏิบัติการอย่างง่าย

ที่นี่ระบบปฏิบัติการทั้งหมดพอดีกับบูตเซกเตอร์ขนาด 512 ไบต์ ( MBR )!

OS ที่เรียบง่ายดังกล่าวหรือคล้ายกันสามารถใช้ในการสร้างกรอบงานง่าย ๆ ที่จะช่วยให้เรา:

ทำให้ภาระ bootloader ภาคต่อมาบนดิสก์เข้าไปในแรมและข้ามไปยังจุดที่จะดำเนินการต่อการดำเนินการ หรือคุณอาจจะอ่านข้อมูลเกี่ยวกับ FAT12, ระบบไฟล์ที่ใช้ในไดรฟ์ฟลอปปี้และดำเนินการที่

อย่างไรก็ตามมีความเป็นไปได้มากมาย ตัวอย่างเช่นเพื่อดูภาษาแอสเซมบลี x86 ที่ใหญ่กว่าเราสามารถสำรวจMykeOS , ระบบปฏิบัติการ x86 ซึ่งเป็นเครื่องมือการเรียนรู้เพื่อแสดงระบบปฏิบัติการจริงแบบ 16 บิตที่เรียบง่ายพร้อมรหัสที่มีความคิดเห็นดีและเอกสารประกอบมากมาย

Boot Loader เป็นแรงบันดาลใจ

โปรแกรมประเภททั่วไปอื่น ๆที่ทำงานโดยไม่มีระบบปฏิบัติการก็เป็น Boot Loadersเช่นกัน เราสามารถสร้างโปรแกรมที่ได้รับแรงบันดาลใจจากแนวคิดดังกล่าวตัวอย่างเช่นการใช้เว็บไซต์นี้:

วิธีการพัฒนา Boot Loader ของคุณเอง

บทความข้างต้นแสดงถึงสถาปัตยกรรมพื้นฐานของโปรแกรมดังกล่าว :

  1. แก้ไขการโหลดหน่วยความจำด้วย 0000: 7C00 address
  2. การเรียกใช้ฟังก์ชัน BootMainที่พัฒนาขึ้นในภาษาระดับสูง
  3. แสดงข้อความ“” สวัสดีโลก…” จากระดับต่ำ” บนหน้าจอ

ดังที่เราเห็นสถาปัตยกรรมนี้มีความยืดหยุ่นสูงและช่วยให้เราสามารถใช้งานโปรแกรมใด ๆไม่จำเป็นต้องเป็นบูตโหลดเดอร์

โดยเฉพาะอย่างยิ่งมันแสดงให้เห็นถึงวิธีการใช้เทคนิค "รหัสผสม"ซึ่งเป็นไปได้ที่จะรวมการก่อสร้างระดับสูง (จากCหรือC ++ ) กับคำสั่งระดับต่ำ (จากAssembler ) นี่เป็นวิธีที่มีประโยชน์มาก แต่เราต้องจำไว้ว่า:

การสร้างโปรแกรมและได้รับแฟ้มที่ปฏิบัติการที่คุณจะต้องคอมไพเลอร์และลิงเกอร์ของ Assembler สำหรับโหมด สำหรับ C / C ++คุณจะต้องเพียงคอมไพเลอร์ที่สามารถสร้างไฟล์วัตถุสำหรับโหมด 16 บิต

บทความนี้ยังแสดงวิธีดูโปรแกรมที่สร้างขึ้นในขณะใช้งานและวิธีการทดสอบและดีบัก

แอพพลิเคชั่น UEFI เป็นแรงบันดาลใจ

ตัวอย่างข้างต้นใช้ข้อเท็จจริงของการโหลดเซกเตอร์ 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 ) ซึ่งช่วยให้เราสามารถเรียกใช้โปรแกรมบนเครื่องคอมพิวเตอร์โดยไม่คำนึงถึงระบบปฏิบัติการและแม้กระทั่งโดยไม่คำนึงถึงสื่อเก็บข้อมูลใด ๆที่เป็น เชื่อมต่อโดยตรงกับคอมพิวเตอร์:

การบูทเครือข่าย (PXE) คืออะไรและคุณใช้งานได้อย่างไร?

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.