Pull to refresh

Comments 37

UFO just landed and posted this here
Хм, а что не так? Все понятно ведь вроде
система, которая есть на любом компьютере — BIOS

Вынужден огорчить, но BIOS встречается уже далеко не везде: например, отсутствует на компьютерах Apple Mac.
Сейчас операционные системы переходят на UEFI.

Во-во. Про Биос и 512 байт уже все кто лень писали. Я было понадеялся, что ну может наконец кто-нибудь через UEFI будет грузиться. Ан нет. Все как в статьях 20 летней (а может и больше) давности

Поддержу ветку. Таких статей ещё до Хабра было полно, правда загрузчик записывали на дискетку, и на реальном железе тестили. Даешь UEFI и операционку на Rust или JavaScript! :)

Цикл статей про OS на Rust под Raspbery Pi. Это одна статья, но я помню, что было еще. И еще статья на хабре.


UEFI (я могу ошибаться, потому что только hello world писал) в общих чертах не сложно. Вам потребуется EDK2 (девелопмент тулкит для TianoCore — Open Source имплементации интерфейса UEFI) и документация на API (возможно, я ошибся и не ту ссылку кинул). EFI-приложение с точки зрения кода — обычная программа на C, только API свое. Потом все собираете согласно манам, полученный .efi-файл кладете куда-нибудь в /EFI/BOOT на диске, загружаетесь в EFI Shell и запускаете. Для x86-64, если я не ошибаюсь, если он называется bootx64.efi, то будет запущен автоматом, если загружаться с того диска, где он лежит.


Никакой магии, ассемблера, 512 байт и int 21. А про сам UEFI интерфейс было и на хабре.

Да, я видел эти статьи поэтому и упомянул тот же Rust. Посыл моего комментария скорее был таким, что писать про «свою ОС» уже следует с оглядкой на современные средства и технологии, а не пересказывать вдоль и поперек изъезженные темы.

Здесь я с Вами соглашусь. Туториалов "пишем свою ОС", начинающихся и заканчивающихся на BIOS-загрузчике полно.

> Командой org 7C00h мы говорим, что код нужно загружать в ОЗУ по адресу 0x7C00.

Я правильно понимаю, что, следовательно, если написать org 666h, то код загрузится в ОЗУ по адресу 0x666? (Спойлер: нет).

Может, автору было просто лень поискать подобные статьи на Хабре?

Учитывая время года — вероятно, публикация на Хабре была условием зачёта.
cli ;Запрещаем прерывания (чтобы ничего не отвлекало)
sti ;Запрещаем прерывания
Не ошиблись в комментарии?
Попробую угадать. Следующая статья будет про загрузку с образа флоппи и работу с fat12. Скажите, какой смысл в этой статье на хабре в 2020 году? Чем вас не устраивали прошлые?
А прошлые были лучше? Я без сарказма или иронии, просто не встречал до этого ни одной похожей, если у Вас есть возможность, то не могли бы вы поделиться ссылкой, пожалуйста?

Их было много… Большинство начинает за здравие, кончает за упокой… Но это не значит, что не надо продолжать работу в этом направлении. Нормальной, а главное, законченной серии статей про написание ОС на хабре лично я не видел.

Так же это и не значит, что надо начинать ту же самую работу, с того же самого начала, с которого её начинали уже много раз до.
В качестве загрузчика лучше уж тот же grub использовать. Зачем велосипедить? Впрочем я недавно сам всё это писал, чисто вспомнить детство, дошел до загрузки ядра и остановился на переходе в защищенный режим, именно потому что подумал, что нет смысла это всё писать, если можно использовать уже готовое решение, к тому же более универсальное.
Лучший способ понять и запомнить, как оно внутри работает, это написать такой хелловорд. «Испачкать руки». Кроме обучающего, другого смысла я не вижу.
Ну это да, я вот тоже не так давно игрался, вспоминая былые времена :) Просто решил что нет смысла дальше возиться с защищенным режимом (который я кое-как помню еще) если сейчас используется long mode.

Заголовок спойлера
use16
org 0h

; ---------------------------------------------------------------------------------------
; Boot
;
; DL - Boot drive number
; ---------------------------------------------------------------------------------------

; Start
    mov     ax, 07C0h
    mov     ds, ax

; Initialize stack
    mov     ax, 9000h
    cli
    mov     ss, ax
    mov     sp, 0FFF0h
    sti

; Set destination segment to load kernel to
    mov     ax, 50h
    mov     es, ax

    mov     si, msg_boot_loading
    call    print_string

; Reset floppy drive
    mov     ah, 0
    int     13h
    jc      .boot_error

; Load kernel into memory
    mov     ax, 0
    mov     bx, sectors_per_track
    mov     cx, heads_per_cylinder
    call    calc_chs

    xor     bx, bx ; es:bx -> buffer
    mov     al, 18 ; sectors count; read 18 sectors of loader
    mov     ah, 2
    int     13h
    jc      .boot_error

; Set data segment
    push    es
    pop     ds

; Pass control to kernel
    push    ds
    push    word kernel_entry
    retf

; Show error
.boot_error:
    mov     si, msg_fail
    call    print_string

.boot_finish:
    hlt
    jmp     .boot_finish

;
; Print String
;
; DS:SI must point to zero-terminated string
;------------------
print_string:
    pushf
    push    ax
    push    bx
    push    si
    cld
    mov     ah, 0Eh
    mov     bh, 0
print_string.repeat:
    lodsb
    cmp     al, 0
    je      print_string.finish
    int     10h
    jmp     print_string.repeat
print_string.finish:
    pop     si
    pop     bx
    pop     ax
    popf
    ret

;
; Calculate CHS address by logical sector address
;
; AX - Logical sector number
; BX - Sectors per track
; CX - Heads per cylinder
; Returns (ready for Int13h:02h call):
; CL - Sector number
; CH - Track number
; DH - Head number
; -----------------
calc_chs:
    pushf
    push    ax
    push    bx
    xor     dx, dx ; prepare dx:ax for operation
    div     bx ; divide by sectors per track
    inc     dl ; add 1 (obsolute sector formula)
    mov     bl, dl
    xor     dx, dx ; prepare dx:ax for operation
    div     cx ; mod by number of heads (Absolue head formula)
    mov     dh, dl ; everything else was already done from the first formula
    mov     ch, al ; not much else to
    mov     cl, bl
    pop     bx
    pop     ax
    popf
    ret

; Data

msg_boot_loading        db 'Loading kernel... ', 0
msg_ok                  db 'Ok.', 0Dh, 0Ah, 0
msg_fail                db 'Failed!', 0Dh, 0Ah, 0

sectors_per_track       dw 18
heads_per_cylinder      dw 2

times 510 - ($ - $$)    db 0
                        dw 0xAA55

; ---------------------------------------------------------------------------------------
; Kernel (real mode)
; ---------------------------------------------------------------------------------------

; CS, DS, and ES now set to 0050h, SS to 9000h

kernel_entry:
    mov     si, msg_ok
    call    print_string

    mov     eax, 0FAFAFDFDh
    shr     eax, 16
    cmp     ax, 0FAFAh
    jne     .fail

    mov     si, msg_ok
    call    print_string

; push 0B800h
; pop es
; xor di, di
; mov word [es:di], 0730h

    nop
    nop
    nop
    nop
    mov     ax, an_address

.finish:
    hlt
    jmp     .finish

.fail:
    mov     si, msg_fail
    call    print_string
    jmp     .finish

; ---------------------------------------------------------------------------------------
; Kernel (protected mode)
; ---------------------------------------------------------------------------------------

; Data

gdt:

; null descriptor
                        dd 0
                        dd 0

; code descriptor
gdt_code_limit_low      dw 0FFFFh
gdt_code_base_low       dw 0
gdt_code_base_middle    db 0
gdt_code_access         db 10011010b
gdt_code_granularity    db 11001111b
gdt_code_base_high      db 0

; data descriptor
gdt_data_limit_low      dw 0FFFFh
gdt_data_base_low       dw 0
gdt_data_base_middle    db 0
gdt_data_access         db 10010010b
gdt_data_granularity    db 11001111b
gdt_data_base_high      db 0

;
; Print Byte
;
; AL - byte to print
;------------------
print_byte:
    pushf
    push    ax
    push    dx
    mov     dl, al
    shr     al, 4
    add     al, 48
    cmp     al, 58
    jl      print_byte.print
    add     al, 7
print_byte.print:
    call    print_char
    mov     al, dl
    and     al, 0Fh
    add     al, 48
    cmp     al, 58
    jl      print_byte.print2
    add     al, 7
print_byte.print2:
    call    print_char
    pop     dx
    pop     ax
    popf
    ret

;
; Print Char
;
; AL - char to print
;------------------
print_char:
    pushf
    push    ax
    push    bx
    xor     bx, bx
    mov     ah, 0Eh
    int     10h
    pop     bx
    pop     ax
    popf
    ret

;
; Print New Line Chars
;
;------------------
print_nl:
    pushf
    push    ax
    mov     al, 0Dh
    call    print_char
    mov     al, 0Ah
    call    print_char
    pop     ax
    popf
    ret

;
; Print Word
;
; AX - word to print
;------------------
print_word:
    push    ax
    mov     al, ah
    call    print_byte
    pop     ax
    call    print_byte
    ret

;
; Print Bytes
;
; DS:SI - pointer to data to print
; CX - number of bytes to print
;------------------
print_bytes:
    pushf
    push    ax
    push    cx
    push    si
print_bytes.repeat:
    mov     al, [si]
    call    print_byte
    dec     cx ; !!
    inc     si
    jcxz    print_bytes.finish ; replace to loop???
    mov     al, 20h
    call    print_char
    jmp     print_bytes.repeat
print_bytes.finish:
    pop     si
    pop     cx
    pop     ax
    popf
    ret

;
; Print Number
;
; AX - number to print
; BL - number base (2, 8 or 10)
;------------------
print_num:
    pushf
    push    ax
    push    bx
    push    cx
    push    dx
    xor     cx, cx
    mov     bh, 0
print_num.repeat:
    xor     dx, dx
    div     bx
    push    dx
    inc     cx
    cmp     ax, 0
    jne     print_num.repeat
print_num.output:
    pop     ax
    add     al, 48
    call    print_char
    loop    print_num.output
    pop     dx
    pop     cx
    pop     bx
    pop     ax
    popf
    ret

;
; Print Decimal Number
;
; AX - number to print
; -----------------
print_decimal:
    push    bx
    mov     bl, 10
    call    print_num
    pop     bx
    ret

an_address:

;
;
;
;
; -----------------



Вообще скорее просто ассемблер хотел вспомнить от нечего делать.
Помню как то раз прыгал из real mode сразу в long mode… Если мне не изменяет память, то это возможно (главное таблицы страниц настроить так, чтобы физические адреса совпадали с виртуальными для кода).
Я что-то такое читал на osdev. Просто с long mode еще не разбирался, а с protected mode игрался еще в старших классах. Ну и я не супероперационку делаю, просто балуюсь :) Хотел вспомнить ассемблер — написал загрузик, вспомнил, ну и забил пока. Будет совсем нечего делать — попробую уже с grub поиграть.

P.S. Ну вот выше как раз ссылку на ту статью дали.
Хм, помню, я много лет назад на заре становления себя писал загрузчик из бут-сектора в лонг-мод, но я вроде бы делал это через защищённый… И думал, что это обязательно или по крайней мере предпочтительно. А оно на самом деле нет?
Там выше ссылка на osdev, где показано как это делается.

Эхх сколько таких циклов было лет 15 назад
90%стопорилось на загрузчике 9% доходили до интерпретатора командной строки и поддержки fat, 0,(9)%доходили до vesa графики
Дошедших до прикладных программ единицы

зануда_mode=true
println('0.(9) == 1 по определению');
зануда_mode=true
0.(9)%*число статей
зависит от числа статей и не всегда равен 1
При всем уважении к автору — писать первичный загрузчик самому — контр-продуктивно. Во-первых, как верно сказали выше, UEFI шагает по планете. Во-вторых, и это самое главное в моем тезисе — лучше изучить спецификацию multiboot и использовать тот же GRUB, сосредоточившись на управлении памятью в вашем ядре. Вот о чем мало полезных статей.

Если поможет — вот мои потуги 2013 года в этом направлении.
Кстати, а разве mov не «тяжелее» xor'а? Зачем ax обнулять xor'ом, а остальные регистры mov'ом?

Потому что xor-ить сегментные регистры нельзя — таких инструкций попросту нет.

Уже не тяжелее, в процессоре они преобразуются в одинаковую внутреннюю инструкцию.

Ностальгия. Вспомнил, как 15 лет назад на WASM писал загрузчик, ядро с переходом в защищённый режим, простенький менеджер процессов. Забросил на этапе реализации виртуальной памяти.

спасибо! жду продолжения! здорово что всё с пояснениями и без сторонних библиотек, хочется понять как всё работает с самого начала

Рекомендую всем, кто хочет пописать OS пройти курс MIT на основе операционной системы JOS. У нас в универе был отличный набор лабораторных работ на основе этого курса.
Sign up to leave a comment.

Articles