UProLa

Неокріпші думки

Hello World, або Мені ОС більше не потрібна, можу написати свою.

with 11 comments

Кожен справжній програмер хоч раз у житті хоче спробувати написати свою ОСьку, але рідко хто доходить навіть до етапу написання загрузчика з диска/флешки/дискетки. Я можу похвалитись тим, що дійшов до емуляції дискетки і виводу рядка на екран віртуальної машини! Якщо хочете повторити мій “подвиг”, починайте читати даний туторіал (повторю, тут описана не ОС, а загрузчик – просто виконання невеликої порції коду на процесорі без всяких там гіндовсів та гінуксів)

Й так, що необхідно для роботи?

Намагатимусь описати крос-платформенні продукти.

  1. Для роботи необхідно знання принципів машинного коду – тобто, список базових інструкцій (MOV INT OR CALL), як користуватись мітками і тп. Хоча, щоб просто запустити код – таких знань непотрібно.
  2. Ассемблер – fASM. Крос-платформовий. Wiki-RUS,Wiki-ENG. (можете використати щось інше, але за результат уже не ручаюсь)
  3. Віртуальна машина – BOCHS. Крос-платформова. Wiki-RUS,Wiki-ENG (можете використати щось інше, але за результат уже не ручаюсь)
  4. Опціонально дизасемблер – під гінукс не знаю хорошого, але під гінду є чудесний IDA Pro.

Власне трохи теорії.

Коли комп включається, біос знаходить boot-drive, завантажує звідти перші 512 байт в пам”ять на адресу 0x7C00 і дає процесору команду jmp 0x7C00. Тобто, теоретично достатньо записати асемблерну програму в перші 512 байт boot-sectorа дискетки/диска/флешки і вона почне працювати.
Нюанси. Бут-сектор повинен мати розмір 512 байт, не більше не менше. Тобто, якщо ваша програма більша – то треба спочатку написати підпрограмку завантаження в пам”ять основної програми і передати вказівник IP на нову адресу.
Бут-сектор закінчується так: 0x55AA. Якщо цього не зробити – БІОС відмовиться працювати далі. Принциповий він….
Вся програма повинна бути записана в бінарних інструкціях процесора – заголовки і call ExitProcess тут не потрібні.
Власне і вся теорія )

hello.asm

; I won't use Ukranian, because of different encoding hells

;OK, this is simple BOOT program. All it does - puts a char on screen

org 7C00h        ; tell compiler the start adress of loaded program
                 ; allways 0x7C00 for booting programs
use16            ; Simplify all

;++++++++++++++++++++
;--   MAIN    -------
;++++++++++++++++++++

main:            ; Main program label
 mov ax,0x0000   ; Setup the Data Segment register
                 ; Location of data is DS:Offset
 mov ds,ax       ; This can not be loaded directly it has to be in two steps.
                 ; 'mov ds, 0x0000' will NOT work due to limitations on the CPU

 mov si, HelloWorld     ; Load the string into position for the procedure.
 call PutStr            ; Call/start the procedure
 jmp $                  ; jmp to current instruction, means single instr forever loop

;--------------------
;--   PutChar -------
;--------------------
PutChar:                ; Label to call procedure
 mov ah,0x0E            ; Special function: put char on text screen
 mov bh,0x00            ; Page number (Ignore for now)
 mov bl,0x07            ; Text attr: 07 = White text, black background.
 mov al,65              ; ASCII character code: char "A"
 int 0x10               ; Call the BIOS video interrupt.
 ret                    ; Return to main program

;--------------------
;--   PutStr  -------
;--------------------
PutStr:         ; Procedure label/start
 ; Set up the registers for the interrupt call
 mov ah,0x0E    ; Special function: put char on text screen
 mov bh,0x00    ; Page number (Ignore for now)
 mov bl,0x07    ;Text attr: 07 = White text, black background.

nextchar:       ; Internal label (needed to loop round for the next character)
 lodsb          ; I think of this as LOaD String Block 
                ; (Not sure if thats the real meaning though)
                ; Loads [SI] into AL and increases SI by one
 ; Check for end of string '0' 
 or al,al       ; Sets the zero flag if al = 0 
                ; (OR outputs 0's where there is a zero bit in the register)
 jz return      ; If the zero flag has been set go to the end of the procedure.
                ; Zero flag gets set when an instruction returns 0 as the answer.
 int 0x10       ; Run the BIOS video interrupt 
 jmp nextchar   ; Loop back round tothe top
return:         ; Label at the end to jump to when complete
 ret            ; Return to main program

;---------------------------
;--   Different data -------
;---------------------------
HelloWorld db 'Pruvit, danbst, tu tse zrobuv!',0   ; 0 means end character

times (512-2-($-7C00h)) db 0    ; OK, here we fill the rest of boot sector with 0
                        ; boot sector MUST be 512 bytes long....
db 055H,0AAH            ; ... and end with 0x55AA signature

image.bin

ОК, тепер у нас є файлик з кодом. Зкомпілемо його в бінарний файлик. (тут і далі використовую batch-скрипти гінди, проблем з переводом їх на гінукс надіюсь не буде )))

c:\fasm\fasm.exe hello.asm image.bin

BOCHS config.txt

Щоб БОХ правильно працював задамо йому конфігурації. Створюємо файлик config.txt в папці БОХу (насправді розширення ролі не грає, це просто для зручності гіндоус-юзерів):

floppya: 1_44= C:\fasm\EXAMPLES\Load\image.bin, status=inserted
floppyb: 1_44= a:, status=ejected
romimage: file=BIOS-bochs-latest
megs: 32
boot: floppy
vga_update_interval: 50000
keyboard_serial_delay: 250
cpu: count=1, ips=500000
mouse: enabled=0
private_colormap: enabled=0
i440fxsupport: enabled=0
clock: time0=0
log: -
panic: action=ask
error: action=report
info: action=report
debug: action=ignore


Замітка. Цей конфіг робочий для БОХу 2.4.2. Для інших версій деякі опції можуть відрізнятись. І ще – поміняйте адресу до файлу image.bin в першому рядку.

run

Для полегшення запуску вірт. машини з даним конфігом я створив батник run.bat:

bochs -q -f config.txt

Все ) Після запуску run.bat я бачу таку картинку:
Piccy.info - Free Image Hosting

Останній важливий крок

Тепер треба записати image.bin на якийсь носій і спробувати завантажитись з нього. Я це поки що робити не буду, оскільки не знаю як правильно замінювати бут-сектор флешки.

Update

Останній важливий крок

Оскільки ми переконались у тому, що наша boot-програмка працює, лишилось перевірити її на справжній платформі. У мене нема флоппі-дисководу, на диск записувати напряжно, тому я вибрав в якості носія свою флешку.
Качаєм програмку (для гінди) – MKBT
Запускаємо її
mkbt -c -x image.bin I:
image.bin – ваш бінарний образ, створений fASMом
I: – ваш флеш-драйв
Дивіться не напутайте з буквою флеш-драйва – а то при кожному включенні компа будете дивитись вашу “hello world” програмку…
Після цього при завантаженні компа затисніть F12 (або що там у вас) і виберіть флешку. У мене вона називалась USB-HDD0.

Ось тепер все!

Гінукс юзери (хоча, і не тільки вони) можуть проглянути код загрузчика GRUB stage1, оскільки в ньому знаходиться багато цікавої інформації про зчитування stage2 загрузчика з диска.

Використана “література”

  1. Загрузочный CD своими руками №1
  2. Загрузочный CD своими руками №2
  3. Hello World Boot Loader by Daniel Rowell Faulkner
  4. WeetHet – Boot from USB Flash drive

Written by danbst

26 Грудня, 2009 at 17:27

Відповідей: 11

Subscribe to comments with RSS.

  1. ; Nice English anyway.

    Спробую якось перед Різдвом повторити. Якщо мій HP доживе. Я правда вже раз пробував:
    , але нічого не вийшло.

    З флешкою ідея класна. Ще б добре було просунутись до завантаження grub, і вияснити як пишеться ядро.

    Лінус до речі, теж добрався до системного програмування на третьому курсі. On January 2, 1991 he purchased an Intel 80386-based IBM PC[10] and spent a month playing the game Prince of Persia before receiving his MINIX copy which in turn enabled him to begin his work on Linux.[4] Хоча йому тоді було вже 32.

    І прикинь, вчора був день радіофізики. Я зайшов до них, а в них там виставка старої техніки. І я бачив живий саморобний ZX Spectrum, який по ідеї мав би грузити програмки з магнітофона. За ненаявністю магнітофона грузився з ноута, на якому грали waw-файли. Сама ОС – якийсь там DOS, клавіатура на 40 кнопок, яка працює в 5 режимах (так і не зміг добратись до Бейсіка, бо написи стерті). Але найкрутіше – він завантажується за секунду, бо ОС і Бейсік вшиті в ПЗУ.

    bunyk

    26 Грудня, 2009 at 19:25

    • >>>живий саморобний ZX Spectrum
      ги, історія )

      >>>Ще б добре було просунутись до завантаження grub, і вияснити як пишеться ядро.
      третє моє посилання веде на сайт “Туторіали по написанню своїх ОС”, і в числі публікацій там розбирається також і ГРУБ

      >>>…як пишеться ядро.
      хмм.. мені зараз цікавіша робота з консоллю/графікою/клавою на найнайнайнижчому портовому рівні. Ядро я навіть не збираюсь писати – мультизадачності, розділення пам”яті, підтримка девайсів. Ну нах таке щастя.

      danbst

      26 Грудня, 2009 at 20:49

      • Ще б добре було просунутись до завантаження grub

        під ..гінуксом нема нічого простіше:

        # mkfs.ext2 /dev/sdb
        # mount /dev/sdb /mnt/flashstick
        # grub-install --root-directory=/mnt/flashstick /dev/sdb
        # qemu -hda /dev/sdb

        ulidtko

        27 Грудня, 2009 at 17:49

  2. На флешку я бы просто записал, скажем:
    dd if=image.bin of=/dev/sdX bs=512 count=1
    И, по идее, все бы заработало. Обратить флешку потом можно было бы форматированием.
    Видишь, как всё получается просто — знай немного магии (0×7C00, 0×55AA) и остальное в твоих руках.

    Алекс

    27 Грудня, 2009 at 10:21

    • Ага, тепер зрозуміло.

      Зчитати MBR
      dd if=/dev/hda of=image.bin bs=512 count=1

      Записати MBR
      dd if=image.bin of=/dev/hda bs=512 count=1

      Тут гінукс й справді гнучкіший. Тільки нарахунок просто – це якщо тільки виводити ХеллоВорлд на екран.

      danbst

      27 Грудня, 2009 at 10:29

    • еее… а про таблицю розділів ви не забули, панове? Найпоширеніший її вид — досівський MBR — зберігає дані про чотири primary partitions десь якраз всередині першого сектору. Звичайно, можна скористатися й іншими схемами розбиття диску (наприклад, GPT перспективна), можна створити й власну схему, з блекдж. Але стандарти є стандарти.

      ulidtko

      27 Грудня, 2009 at 17:37

      • мабуть, в даному випадку не говориться про зберігання всіх партицій та даних на носії. Особисто мені було цікаво відмовитись від абстракцій “файл” і “файлова система” та працювати напряму з секторами. А раптом я придумаю якусь структуру кращу за існуючі? =)))

        *через кілька днів у мене пройде ця манія і я стану знову тверезо дивитись на речі*

        danbst

        27 Грудня, 2009 at 19:23

  3. ну, і невеличкий загальний коментар. Мати навички роботи з дизасемблером важливо для загального комп’ютерного розвитку; тим не менше, це не необхідний скіл при написанні своєї ОС.
    І так, IDA Pro я теж рекомендую.

    ulidtko

    27 Грудня, 2009 at 17:55

    • Уміння написати дизасм на асмі – хороша навичка при відлагодженні своєї ОС. Якраз тут навички для роботи з дизасмом знадобляться )))) Хоча так, скіл це не основний.

      danbst

      27 Грудня, 2009 at 19:17

  4. Якщо є проблеми з запуском віртуальної машини в Гінуксі, то можете а) погуглити б) почитати даний лог переписки з джаббера

    bunyk (00:45:40 10/01/2010)
    а в мене навіть твій загрузчик не запустився

    bunyk (01:03:43 10/01/2010)
    ========================================================================
    Bochs x86 Emulator 2.4.1
    Build from CVS snapshot on June 7, 2009
    ========================================================================
    00000000000i[ ] LTDL_LIBRARY_PATH not set. using compile time default ‘/usr/lib/bochs/plugins’
    00000000000i[ ] BXSHARE not set. using compile time default ‘/usr/share/bochs’
    00000000000i[ ] reading configuration from bochs.conf
    00000000000i[ ] lt_dlhandle is (nil)
    00000000000p[ ] >>PANIC<< dlopen failed for module 'x': file not found
    ========================================================================
    Event type: PANIC
    Device: [ ]
    Message: dlopen failed for module 'x': file not found

    A PANIC has occurred. Do you want to:
    cont – continue execution
    alwayscont – continue execution, and don't ask again.
    This affects only PANIC events from device [ ]
    die – stop execution now
    abort – dump core
    debug – hand control to gdb
    Choose one of the actions above: [die]

    danbst (01:16:33 10/01/2010)
    n Wed, Jul 28, 2004 at 04:39:16PM +0200, Johann Spies wrote:
    > I am also trying out bochs. After creating a bximage (flat file) and
    > copying the line indicated by bximage to /etc/bochs-init/bochsrc the
    > following happened when I tried bochs:
    […]
    > Please choose one: [2] 5
    > 00000000000i[ ] lt_dlhandle is (nil)
    > 00000000000p[ ] >>PANIC< >>found

    Install the package bochs-x or change your bochsrc until it stops
    attempting to use x as the display_library. bochs does not like certain
    combinations of config_interface and display_library and will try to use
    the display_library ‘x’ if it doesn’t like the combination.

    Also, you should tell bochs where to look for its config file, even
    though you are using /etc/bochs-init/bochsrc as bochs appears to not
    find it, but bochs does find ~/.bochsrc.

    danbst (01:17:18 10/01/2010)
    Install the package bochs-x

    bunyk (01:17:42 10/01/2010)
    а під x вони мають на увазі графічну систему unix?

    danbst (01:17:46 10/01/2010)
    так

    bunyk (01:22:42 10/01/2010)
    ооо

    bunyk (01:22:47 10/01/2010)
    Device: [MEM0 ]
    Message: ROM: couldn’t open ROM image file ‘BIOS-bochs-latest’.

    danbst (01:23:01 10/01/2010)
    чудесно, це вже набагато краще )

    danbst (01:27:55 10/01/2010)
    romimage: file=”~/Documents/bochs-2.3.7/bios/BIOS-bochs-legacy”, address=0xf0000
    vgaromimage: file=”/home/mayank/Documents/bochs-2.3.7/bios/VGABIOS-lgpl-latest”

    vga: extension=vbe

    ось, знайшов якісь рядки з чийогось робочого конфігу

    bunyk (01:33:27 10/01/2010)
    значить це вставити кудись в конфіг, замінивши файли на наступні?
    bunyk@bunyk-laptop:/usr/share/bochs$ ls
    BIOS-bochs-latest BIOS-bochs-legacy BIOS-qemu-latest keymaps VGABIOS-lgpl-latest

    danbst (01:33:39 10/01/2010)
    так

    bunyk (01:39:46 10/01/2010)
    Pruvit, danbst, tu tse zrobuv!

    danbst (01:39:59 10/01/2010)
    не варто було мене хвалити

    danbst

    10 Січня, 2010 at 09:52

  5. Ну, тут занадто довго розписано.
    А повний алгоритм простіший:
    1. Ставимо пакет bochs
    2. Ставимо пакет bochs-x без якого будуть проблеми
    3. Качаємо http://flatassembler.net/download.php
    4. Створюємо якусь папку типу myos, розпаковуємо туди ассемблер.
    5. Створюємо (Ctrl+C) код загрузчика наприклад в файлі myos/myos.asm
    6. створюємо там же конфігурацію bochs наприклад bochs.conf.
    пишемо там з відповідними замінами назв та адрес:

    floppya: 1_44=/home/bunyk/myos/image.bin, status=inserted
    romimage: file=/usr/share/bochs/BIOS-bochs-latest
    megs: 32
    boot: floppy
    vga_update_interval: 50000
    keyboard_serial_delay: 250
    cpu: count=1, ips=500000
    mouse: enabled=0
    private_colormap: enabled=0
    i440fxsupport: enabled=0
    clock: time0=0
    log: -
    panic: action=ask
    error: action=report
    info: action=report
    debug: action=ignore
    

    7. Щоб багато не морочитись з консоллю створюємо файл наприклад run:

    /home/bunyk/myos/fasm/fasm /home/bunyk/myos/myos.asm /home/bunyk/myos/image.bin
    bochs -q -f /home/bunyk/myos/bochs.conf
    

    Я прописав там абсолютні адреси, бо якось захотілось запустити ~/myos/run з одної далекої-далекої директорії, а воно сказало що не бачить деяких файлів.
    І якщо ви не забули chmod +x run, тоді:
    8. ./run Поїхали!

    8,05. Ну, і на любителя Vim:
    :map :w :!./run

    bunyk

    10 Січня, 2010 at 12:28


Залишити відповідь на danbst Скасувати відповідь